很久以前,很认真地学习了html、css、es6,上手了颇为超前的MVVM框架React,
那时候很喜欢React中纯组件化地概念,尤其是React Router的设计,并且,Redux所推崇的
函数式也是吹牛逼的资本。
最近又重回前端,写了两个小网站,试了试Vue和全新的Typescript,以及api管理工具rap和yapi。稍作整理。
vue启动
现在已经很少有人,从零搭建项目了,也就是自己配置webpack,安装一些开发插件。
这些配置主要涵盖以下几点:
- 开发时的lint,语言特性支持。
- 编译流程,尤其是使用到js新特性,都会用babel翻译一遍,然后像less、sass等样式配置,
也需要对应的loader进行翻译。 - 开发时用的本地server,可以在本地运行一个web server,供开发者查看网页效果。
- 热加载插件,每次修改代码,就马上重新编译,刷新页面。有时候甚至只是局部刷新,并不重新加载页面。
- 打包发布,这里还得包括用到的图标、图片,一些文本。通常会配置多个版本,不同之处在于是否压缩,是否携带debug信息。
使用vue cli可以很轻松地构建一个具备这些功能的开发环境。
1 | npm i -g @vue/cli |
选择手动控制可以细粒度地选择一些特性,
我一般会打开typescript、eslint。
项目结构
1 | ├── .browserslistrc |
几个配置文件都相当短,vue的插件封装了许多功能,
有点类似java里的springboot-compiler-plugin。
DOM树
Vue给用户看到的也就是一个树状结构的组件视图,一般叫做虚拟DOM树。
从代码来看,index.html里放了一个带id的标签,然后js里,
vue的mount函数,使用了这个id。整个由vue构建的视图就插入到了这个标签中。
初始化vue的时候,router和全局状态管理vuex就被传递了进去。
1 | new Vue({ |
1 | <template> |
因此,App.vue就相当于是Root。
这里就可以按照html的写法,组织标签了。
组件
自定义组件
不同的是,vue的template里可以使用各种自定义组件。
如上文中的<a-xxx>
标签,都是引用自antd的组件。不过实际上引入antd用的是Vue.use(),
按官方说法,这是引入的plugin,会调用目标的intall方法,相当于一个hook。全局组件的注册使用Vue.component( id, [definition] )
。
我们自己写的组件,一般不会注册到全局,那样比较混乱。只有基础、常用的组件会注册到全局,
而这些组件都会有各种库提供。
引入自己写的组件可以用import语句,直接引入那个vue文件。然后放到components属性下。
1 | import CustomComponent from '@/src/components/custom.vue' |
组件属性绑定
正如在代码中看到的,每个标签都或多或少有一些属性值。
属性的传入直接在标签上填写即可,代表的是一个常量。
如果要传递变量,就需要使用vue提供的特殊方法。
常用的有v-bind、v-once、v-on。具体含义参考手册。
1 | <blog-post v-bind:title="post.title"></blog-post> |
对于组件自身,props可以直接访问,就如同它自己的属性一样。
1 | const AceEditorProps = Vue.extend({ |
全局中心存储
在我看来,只有props需求的组件,复用性很好,即插即用。
但真正做一个网页,或者说一个项目的时候。很多页面是单例模式:
组件或许可以复用,但在这个项目里,它绝对只会出现一次,数据也只有一份。
比如说,购物车,网页主页,个人中心。
这些页面显然也是由组件堆叠起来的,但它们整体是不可复用的,也不需要复用。
当你想要制作两个购物车页面,那就得有两份不同的购物车数据,两份路由,
这是不可复用的根源,同时也没有必要做这种事情。
因此,这些数据是唯一的情景下,很适合将数据抽离出来,存放到全局存储中。
某种程度上,这也是无奈之举,谈不上多好的设计,是一个非常自然的想法:
数据是唯一的,那就必然要交给一个唯一的对象去管理。
如果类似组件的属性那样管理,这个数据传递起来会非常麻烦,
子组件都必须通过props层层传递,而修改的入口也没有限定。
正因为有种种不便之处,才开发出一个中心存储,再通过一些奇技淫巧,
使得所有组件都能访问到这个中心存储。
Vue的中心存储叫做Vuex。
提供Store、Mutation、Action、Module四个接口。
Store非常好理解,就是刚才说的中心存储。
1 | this.$store.state.p |
像这样访问属性的方式,就可以访问store的数据。
如果想把这些数据使用到组件中,那么需要再套一层。
1 | computed: { |
根据vuex的要求,修改属性一定要通过mutation,不能直接给state赋值。
从原理上说,修改属性这个操作,是必须要被监控到的,因为它可能触发DOM的改变。
通过mutation来修改属性,就有了一个明显的切面去监控数据变化。
但是实际上实现这个监控,手法有很多,也是有可能实现成,直接赋值,也被监控到的。
不过,使用mutation还有一个好处是,数据的改变方式是固定的,甚至可以加上类型限定。
1 | const store = new Vuex.Store({ |
Mutation都是同步方法,如果想要实现异步接口,就得用Action。
Promise就不展开讲了。
经过我尝试,async也是可以用的。
1 | actions: { |
Module就是提供树状层次,让状态不必全部集中到一个入口上,而是分散在不同模块中。
路由
Vue的路由和React差异很大。React自Router 4.0之后,Router就和普通组件一模一样,
直接嵌入到页面中即可。
Vue的路由采取的是slot的方式。先填一些占位符,再在Router初始化时,统一定义每个占位符。
1 | <template> |
1 | import Info from '@/views/FunctionInfo.vue' |
嵌套路由就是在子组件里面也放上<router-view>
,在routes定义中使用children
。
详情看手册把,不赘述了。
路由的改变也提供了一个封装的link组件。
但实际上就是通过this.$router
所携带的各种方法操作路由。
详情见手册。