打开设计稿,先把底部的导航栏给做出来!

我们透过 hash 模式来确定每个页面的路径
注意,用户乱输入路径就会进入到「404」页面!
Tips:
git reset --hard HEAD:可以把当前在工作区里的所有变动给删掉…… -> 你写了无意义的代码,不想一个个删,那么你就重置一下吧!import -> 我测试未成功,所以我只好手动 import 了 -> 感觉 Webstorm 更香!自动找到 index 文件:

切换路径展示不同的内容:
router 传给 new Vue()router-link,用点击的方式来切换路由,而不是在地址栏里输入不同的路径来切换路由……代码:创建三个页面 · ppambler/vue-morney@33c34f1
效果:

关于把代码提交到远程分支:
git add .
git commit '创建三个页面'
git push origin morney-nav:morney-nav

接下来要做的:
这个「目前这样做」指的是「创建三个页面」这个提交代码里的做法!
实际工作中做项目,leader 是不怎么允许你犯错误的!但是自己在做项目的时候,其实犯得错误越多越好!
为啥这样说呢?
因为:

都犯完了,那么这就是「领域专家」了!
举例来说,目前我们做的这个项目,「导航栏」功能面临着两条路可选:

App.vue 的导航栏 -> 如果「404」页面不用导航栏呢?难道我们要写一个v-if判断一下路径吗?(这样做很麻烦)引入组件的两种姿势:
Nav组件 -> 很麻烦main.ts -> 全局注册一个Nav组件代码:全局组件 <Nav/> · ppambler/vue-morney@ed7e003
每一条路都走了,那就是经验加成……

如何确定哪个决策是对的? -> 把所有路都走一遍,权衡优缺点,选择自己目前认为非常合适的做法!
市面上大部分的前端课程,都直接告诉你走图上那条我们最终选择的「第三条路」,而前边两条有错的路是不会告诉你的……
而这就会导致这样的问题:
在面试的时候,面试官经常问「你遇到过一个比较难的技术问题是什么?」
面对这样的问题,你肯定会回答「没有遇到过什么比较难的技术问题」,毕竟,你所接触到的例子,都是别人直接告诉你答案的……
所以,对于方方讲的这个「引入Nav」的例子,我们就可以回答说:
App.vue引<Nav />,发现这样做有很多缺点,如……(把缺点说出来)<Nav />
options.components 引入,但这种姿势也有 xxx 这样的缺点main.ts里边,全局注册Nav 组件 -> 对边其它的做法,这种更方便……面试官的感受 -> 在你表达的过程中,会带入进去,最后,给出评价「思考全面」
虽然最终所选择的方案,并不难,但这所谓的难是难在这过程足够曲折……
难不难不在于结果难,而在于选择这个最终结果的过程足够曲折……
一个提交到远程分支上的技巧:
tgp1() {
git add .
git commit -m "$1"
git push origin $2:$2
}
效果;

目前,我们已经确定了该如何加导航栏了,那么接下来就是「加样式」!

用户输入一个错误的网址,结果得到的结果是一片空白,所以我们需要提示用户「你输入的这个网址有问题!」
NotFound.vue -> 提示信息+返回首页NotFound组件呗!路由表里边的路径匹配是这样的:

先匹配前边 4 个,如果这 4 个都不成功,那就是「
*」了!
返回首页有两种姿势:
<!-- 一般选择这种高级姿势 -->
<router-link to="/">返回首页</router-link>
<a href="#/">返回首页</a>
一般来说,能使用高级姿势,那就使用高级姿势!
代码:添加 404 页面 · ppambler/vue-morney@fe8cc88
效果:

最好把这两条路都走一遍……当然,如果你时间不够,那就使用正确答案——Flex 布局!

根据设计稿,让导航栏在视口的最底部,我们有俩种姿势可以做到:
关于 Nav组件的 class 名 -> 不要叫site-nav之类的,直接用一个最简单的名字nav,然后再加上 scoped 就好了! -> scoped的原理:「在元素上加个随机属性data-v-x1」+「属性选择器.nav[data-v-x1]」 -> 不加scoped的话,那就是直接.nav了!
做法:
Money.vue上下布局 -> content+nav -> 全局样式写在App.vue(该组件不要加scoped,不然样式就有局限性了)代码:Demo
效果:

这样做的问题:
其它页面也要这样做 -> 代码需要复制两遍 -> 假如Money的样式写错了,我们除了要改Money的样式以外,还得再改两遍(标签页面、统计页面) -> 假如有 20 个页面也用到了,那么就得再改 20 遍……
重复三次就会有两次 bug 的可能性……
重复三次布局:Demo
方方的理念:我与重复不共戴天,重复在,我就不在,我在,重复就不在,我们俩个不能杵在同一片天下


三个页面的 HTML 结构是重复的(content除外),而且样式也是重复的
所以,我们能否把这俩重复部分提到另外一个组件去呢?
插槽:其它组件引入这个
Layout组件,把内容插到这个Layout组件去!
做法:
LayoutNav组件一样(其实可以不用全局注册Nav组件,直接把该组件注册到Layout里边去就好了!)Layout组件,把样式都删了,把不同的内容写到Layout标签去就好了!代码:Demo
代码整体上看,变得好简洁。工程师与码农的区别是:
方方一直强调「初级程序员就是调 API,中级程序员就是封装」
如果你有把重复的代码封装成组件的话,如Layout组件这样,那么你就比低级程序员高级很多了!
有时候 webstorm 有红色的小波浪线是因为它慢了卡了……过一会儿就没事儿了
小结:
编程就咩有复杂的东西,因为编程的最终目的就是把复杂的东西变简单!
如:

svg-sprite-loader 引入 icon最新的前端做的 icon 不用什么雪碧图,而是直接用 svg icon
.svg.svg放到assets/icons目录里边(一般先放在这个目录,当然,你也可以放在public目录)svg 是 xml 代码,话说,我们该如何把这些 svg icon 引入到我们的项目中去呢?
我们无法直接在 ts 中引入 svg 图标,不然会报「ts cannot find module svg」这样的错误 -> 配置一下:
// 在 shims-vue.d.ts 里边追加
declare module "*.svg" {
const content: any;
export default content;
}

我们想要的是 svg use 的使用方法,而不是直接一个路径字符串……
所以如何做?
我们需要一个 loader——svg-sprite-loader
安装 loader:
npm install svg-sprite-loader -D
# via yarn
yarn add svg-sprite-loader -D
安装成功后,我们需要配置一下,但是这些配置是webpack.config.js的,而我们这个项目是用vue.config.js来配置的 -> 把官方提供的 webpack 配置按照 vue-cli 文档翻译成vue.config.js支持的!
配置代码:
// vue.config.js
const path = require("path");
module.exports = {
lintOnSave: false,
// 函数
chainWebpack: (config) => {
// __dirname root 目录 -> vue-morney/
const dir = path.resolve(__dirname, "src/assets/icons");
// config 是 vue 把 webpack API 封装之后暴露给我们使用的
config.module
.rule("svg-sprite")
.test(/\.svg$/)
.include.add(dir)
.end() // 只包含 icons 目录,其它目录旗下的 .svg ,我不管
.use("svg-sprite-loader") // use 一遍,再重复一遍……
.loader("svg-sprite-loader")
.options({ extract: false }) // 不要把它解析出文件来
.end();
// 配置插件 -> 文档上抄的!
config
.plugin("svg-sprite")
.use(require("svg-sprite-loader/plugin"), [{ plainSprite: true }]);
config.module.rule("svg").exclude.add(dir); // 其他 svg loader 排除 icons 目录,毕竟之前已经走过来了,不然就冲突了……
// config.module
// .rule('svg-sprite')
// .test(/\.(svg)(\?.*)?$/)
// .include.add(dir).end()
// .use('svg-sprite-loader-mod').loader('svg-sprite-loader-mod').options({extract: false}).end()
// .use('svgo-loader').loader('svgo-loader')
// .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]}))
// .end()
// config.plugin('svg-sprite').use(require('svg-sprite-loader-mod/plugin'), [{plainSprite: true}])
// config.module.rule('svg').exclude.add(dir)
},
};
再次使用的效果:

之前用 iconfont 的时候,我们是直接引入一个xxx.js文件的

在页面上引入了symbol标签之后(import x from "@/assets/icons/money.svg";就是在做把symbol标签引入到body这件事儿 -> 如何做到的? -> loader 帮我们做的),我们就可以use svg 了!
loader 做的事儿 -> 把
money.svg变成symbol标签,然后放到svg标签这个舞台上!(会有很多个symbol放在舞台上,只要你import了,并且在 JS 中用到了) -> 把svg标签放到body里边
use是 xml 语法,默认还是用自闭合比较好!
import y from "@/assets/icons/label.svg";
console.log(x);
console.log(y);
<svg>
<use xlink:href="#money" />
</svg>
效果:

以上做法就实现了一种非常好用的引入 icon 的一种机制!
不过,这需要解决两个工程问题:
import一个 icon,可假如我有 20 个 icon 呢?那么我岂不是要import 20 下? -> 我能否一下子就把icons目录下所有的xx.svg都给引入了呢?……xlink:href…… 这样…… -> 我们能否把它简化一下,封装成一个组件?我觉得还是使用外链的方式比较好!因为这比较好修改呀!而不是自己动手一个个下载 svg 图标!万一图标效果不好呢?岂不是还得修改一下,再重新下载? -> 由于这个项目是会用到很多 icon 的,而不是仅仅就只有这 3 个 icon,所以我最后应该会改成外链的形式 -> 或许方方之所以这样讲是因为设计师会直接提供给我们一个个 svg icon,而不需要我们到 iconfont 里边找……


鸵鸟遇到危险就把头埋到地里边 -> 没有危险了!
看电视说,吸烟有害健康,引起肺炎,一咬牙就不看电视了 -> 没有危险了!
我们虽然可以让 webstorm 关掉提示的,但提交代码的时候也会检查的呀! -> 所以我们可以这样做:

命令行关提示的意思是 -> 我们写上了注释,让命令行忽视了这个文件的检查,也就是「怎么做到命令行关提示」
但真正的做法,是把代码错误给真正解决了!而不是让我们无视它就行了!
改代码 -> 不用require,改用import -> 但是有些 node 版本是不支持import的

所以,第三种姿势是「改配置」:

最好的方式是改代码 -> 而改代码需要你去看 ESLint 的文档!当然,webstorm 的提示也可以帮到你!
ESLint 有几百项配置 -> 用上你的「狗手和狗眼」就能把报错给 GG 了!而不是让方方教了如何把具体的某个错误给 GG 了!
姿势优先级选择:
改代码 > 改配置 > 鸵鸟做法
我现在的状况挺像鸵鸟 -> 以为逃避就能解决问题……
我们不想
import一个个 svg icon! ->import一个目录,所有icon就一次性引入了……
代码:
// Nav.vue
let importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
requireContext.keys().forEach(requireContext);
try {
// 不支持@,只能用相对路径
importAll(require.context("../assets/icons", true, /\.svg$/));
} catch (error) {
console.log(error);
}
上边这个代码 -> 经验告诉我们,在做单元测试的时候会失败,所以需要加个
try……catch……-> 有错误就是把错误打出,而不是让代码运行中断!
效果:

这个代码哪儿来的?
方方搜遍全网搜到的 -> 初级程序员调别人的代码 API,中级程序员就是封装一些代码,高级程序员就是只要这个互联网上存在它的解决方案,我就一定能找到,即使互联网上没有,我也可以找人问到
没有两年经验是搜不到的 -> 任何难题都能想办法解决的这种能力!
小结:
解决完第一个问题后,接下来解决第二个问题——封装 icon

Icon.vueimport目录的代码也搞过来Icon -> 注意,全局注册的组件不能太多,这一点一定要控制住!<Icon name="xxx" /> -> 所以在定义Icon组件的时,得搞一个形参name代码:Demo
效果:

在提交代码时,ESlint 检测出错了!

做法:
// 在 .eslintrc.js 的 rules 选项里边追加两个配置!
{
rules: {
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-explicit-any": 0,
}
}
可以看到,很多时候,我们就是在把重复代码创建成一个组件就搞定了!
接下来要做到的:美化Nav(会重新命名div)
不要追求先讲一遍再用,就像是不要「放大招前吟唱」 -> 不要先学一遍
scss,再去写scss,边用边学……不然,学一遍后再去用,很容易忘记自己之前学了什么……
Nav.vue -> 把 root div 改成 navrouter-link不是一个真正意义上的标签(咩有 router-linl {} 这种姿势) -> 添加 class item 加样式 -> <a class="item"></item>item的高度 -> 不要写死(内容填充) -> 在body里边给个默认行高 1.5(确定了,就不用每处都写上了)<Icon />默认就有个.icon(svg 标签上有.icon),不需要<Icon class="icon" /> 这样用阴影的技巧就是不能让别人看出你用了阴影……就像是女生化妆了,但是看不出来,那这这就是牛逼!
代码:Demo
效果:

接下来做 -> 激活路由的时候,如何把对应的 icon 高亮!
active-class 的使用路由激活 -> 点了一个链接,那么这个链接就应该高亮起来!
router-link 提供的 active-class 参数来搞(假设值为x) -> 当前页面的路由与 router-link 的 to 值一样的话,那么该router-link就会有个叫x的class -> <a class="x"> -> 如何做到的?每次路由切换都会刷新整个页面(包括底部的Nav) -> 动态匹配class代码:Demo
效果:

如果路由并不完全相同,我也要激活,这一点该做怎么做呢? -> 如
to="x-money"之类的 -> 用到再说!
vue 加上的
viewport不够完全! -> 抄手淘的
<!-- 改 public/index.html -->
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
我顺便把label.svg的大小也改了!
改的过程很简单:

代码:Demo
➹:深入理解 flex-grow、flex-shrink、flex-basis - 掘金
➹:Unable to import svg files in typescript - Stack Overflow
➹:JetBrains/svg-sprite-loader: Webpack loader for creating SVG sprites.
➹:懒人神器:svg-sprite-loader 实现自己的 Icon 组件 - 好好写代码吧 - SegmentFault 思否
➹:未来必热:SVG Sprites 技术介绍 « 张鑫旭-鑫空间-鑫生活
➹:基于 vue-cli3 使用 svg-sprite-loader 在 vue 中引入 svg 图标 - front-gl - 博客园
➹:VUE-cli3 使用 svg-sprite-loader - 掘金
Nav/Layout/Icon……