VUE 是前端三剑客之一, 以前一直处在写过 demo 的地步, 没有很深刻的体会, 此次公司由于招不到 react 的人, 因此但愿尝试将技术栈换为 vue, 以便更方便招人.javascript
因而就有了 VUE 的踏坑之旅, 并且一开始就是配合 ts 来食用, 由于在 react 中 ts 的配合至关的好, jsx 里的代码类型提示应有尽有, 能够很大的提高开发效率以及减小 bug. 觉得在 VUE 也能获得这样好的体验.html
可是....前端
VUE 虽然也能够支持 JSX, 官方仍是推荐使用模板渲染, 而模板里压根没有代码提示, 因此 ts 的功力就废了一半(剩下的一半还在 script 里), 具体的讨论请参考vue
前戏差很少了, 开始步入正题.java
安装 vue cli 3.x
这个脚手架能够快速启动项目 yarn global add @vue/cli
推荐全局安装.node
在任意文件夹下 vue create .
. 会在当前文件夹中注入初始项目脚手架, 包含 webpack, babel, ts, lint, jest, jsdoc 等一整套模板react
使用 ts 会默认使用 vue-property-decorator
这个库 github, 这个库用装饰器的写法, 将不少 js 中繁杂的配置抽象出来, 一开始不习惯. 习惯了能够大大提升效率的.linux
vue-property-decorator
的食用方法这一个是与另外一个 vue 的库 vue-class-component同样的用法. 这个装饰器库源自 class 库, 只是再封装了一层, 使代码更为简洁明了. options 里面须要配置 decorator 库不支持的属性, 哪些是不支持的呢? 那就请看彻底文, 凡是没写的都是不支持的. 好比components
, filters
, directives
等webpack
表示该组件引入了哪些子组件git
<template>
<div id="app">
<HelloWorld />
</div>
</template>
<script lang="ts"> @Component({ components: { HelloWorld, // 声明子组件的引用 } }) export default class App extends Vue {} </script>
复制代码
filter 表示对数据的筛选, 跟 linux 中的管道符十分类似, 数据经过 filter 进行处理变成新的数据.
注意, 在这里配置时必定要使用 filters, 不要忘了 s, 不然不会报错可是也没做用.
<template>
<div>{{msg | addWorld}}</div>
</template>
<script lang="ts"> @Component({ filters: { addWorld: (value: string) => `${value} world`, }, }) export default class App extends Vue { private msg = 'Hello' // filter 以后显示 hello world } </script>
复制代码
具体的介绍能够看 Vue 的官方介绍. 简单来讲就是 DOM 节点在一些特定钩子触发时添加一些额外的功能
钩子函数有:
若是bind 和 update 时调用同样能够进行简写, 不用指定某个钩子
钩子函数参数:
v-demo="1+1"
为 "demo"v-demo="1+1"
为 2v-demo="1+1"
为 "1+1"v-demo:foo
为 'foo', 注意要在 modifier 前使用 arg, 否则会将 arg 做为 modifier 的一部分, 如v-demo.a:foo
arg 为 undefined, modifier 为{'a:foo': true}
v-demo.a
这个值为 {a:true}
看个简单的实例:
<template>
<span v-demo:foo.a="1+1">test</span>
</template>
<script lang="ts"> @Component({ directives: { demo: { bind(el, binding, vnode) { console.log(`bindingName: ${binding.name}, value: ${binding.value}, args: ${binding.arg}, expression: ${binding.expression}`); // bindingName: demo, value: 2, args: foo, expression: 1+1 console.log('modifier:', binding.modifiers); // {a:true}, 没法转为 primitive, 因此单独打印 }, }, demoSimplify(el, binding, vnode) { // do stuff }, }, }) export default class App extends Vue {} </script>
复制代码
父子组件传递数据 props的修饰符, 参数能够传
String, Number, Boolean
Invalid prop: type check failed for prop "xxx". Expected Function, got String with value "xxx".
@Prop() name = 1
来表示默认值 1, 虽然看起来同样, 可是会在 console 里报错, 不容许修改 props 中的值[Vue warn]: Missing required prop: "xxx"
(value) => value > 100
父组件:
<template>
<div id="app">
<PropComponent :count='count' />
</div>
</template>
<script lang="ts"> @Component({ components: {PropComponent} }) class Parent extends Vue { private count = 101 } </script>
复制代码
子组件:
<template>
<div>{{count}}</div>
</template>
<script lang="ts"> @Component export default class PropsComponent extends Vue { @Prop({ type: Number, validator: (value) => { return value > 100; }, required: true }) private count!: string // !表示有值, 不然 ts 会告警未初始化 } </script>
复制代码
与 Prop 的区别是子组件能够对 props 进行更改, 并同步给父组件,
子组件:
<template>
<div>
<p>{{count}}</p>
<button @click="innerCount += 1">increment</button>
</div>
</template>
<script lang="ts"> @Component export default class PropSyncComponent extends Vue { @PropSync('count') private innerCount!: number // 注意@PropSync 里的参数不能与定义的实例属性同名, 由于仍是那个原理, props 是只读的. } </script>
复制代码
父组件: 注意父组件里绑定 props 时须要加修饰符 .sync
<template>
<PropSyncComponent :count.sync="count"/>
</template>
<script lang="ts"> @Component({ components: {PropSyncComponent} }) export default class PropSyncComponent extends Vue { @PropSync('count') private innerCount!: number // 注意@PropSync 里的参数不能与定义的实例属性同名, 由于仍是那个原理, props 是只读的. } </script>
复制代码
也可结合 input 元素的 v-model
绑定数据, 实时更新. 由读者自行实现.
监听属性发生更改时被触发. 可接受配置参数 options
immediate?: boolean
是否在侦听开始以后当即调用该函数deep?: boolean
是否深度监听.<template>
<div>
<button @click="innerName.name.firstName = 'lorry'">change deeper</button>
<button @click="innerName.name = 'lorry'">change deep</button>
</div>
</template>
<script lang="ts"> @Component export default class PropSyncComponent extends Vue { private person = { name: { firstName: 'jiang' } } @Watch('person', { deep: true, }) private firstNameChange(person: number, oldPerson:number) { console.log(`count change from${oldName.name.first}to: ${oldName.name.}`); } } </script>
复制代码
event?: string
, 若是没有的话会自动将 camelCase 转为 dash-case 做为事件名.event
, 会在返回值以后, 也就是第三个参数.子组件:
<template>
<div>
<button @click="emitChange">Emit!!</button>
</div>
</template>
<script lang="ts"> @Component export default class EmitComponent extends Vue { private count = 0; @Emit('button-click') private emitChange() { this.count += 1; return this.count; } } </script>
复制代码
父组件, 父组件的对应元素上绑定事件便可:
<template>
<EmitComponent v-on:button-click='listenChange'/>
</template>
<script lang="ts"> @Component({ components: { EmitComponent, }, }) export default class App extends Vue { private listenChange(value: number, event: any) { console.log(value, e); } } </script>
复制代码
跟 react 中的同样, ref 是用于引用实际的 DOM 元素或者子组件.应尽量避免直接使用, 但若是不得不用 ref 比 document 拿要方便不少, 参数传一个字符串refKey?:string
, 注意这里若是省略传输参数, 那么会自动将属性名做为参数, 注意与@Emit
的区别, @Emit
在不传参数的状况下会转为 dash-case, 而 @Ref
不会转, 为原属性名
<template>
<div>
<span>Name:</span>
<input type="text" v-model="value" ref='name' />
</div>
</template>
<script lang="ts"> @Component export default class RefComponent extends Vue { @Ref('name') readonly name!: string; private value = 'lorry' private mounted() { console.log(this.inputName); // <input type="text"> // do stuff to ref } } </script>
复制代码
其本质是转换为 inject
和 provide
, 这是 vue 中元素向更深层的子组件传递数据的方式.二者须要一块儿使用.与 react 的 context
十分的像.
任意代的子组件:
<template>
<span>Inject deeper: {{bar}}</span>
</template>
<script lang="ts"> @Component export default class InjectComponent extends Vue { @Inject() private bar!: string private mounted() { console.log(this.bar); } } </script>
复制代码
任意祖先元素:
<script> export default class App extends Vue { @Provide() private bar = 'deeper lorry' } </script>
复制代码
方便不少, 若是为了不命名冲突, 可使用 ES6 的 Symbol
特性做为 key, 以祖先元素举例:
须要注意的是避免相互引用的问题, symbol 的引用最好放到组件外单独有个文件存起来.
export const s = Symbol()
复制代码
父组件:
<script> export default class App extends Vue { @Provide(s) private bar = 'deeper lorry' } </script>
复制代码
子组件:
<script> @Component export default class App extends Vue { @Inject(s) private baz = 'deeper lorry' } </script>
复制代码
@ProvideReactive/@InjectReactive
顾名思义就是响应式的注入, 会同步更新到子组件中.好比下例能够实如今 input
中的输入实时注入到子组件中 父组件
<template>
<div id="app">
<input type="text" v-model="bar">
<InjectComponent />
</div>
</template>
<script> @Component({ InjectComponent }) export default class App extends Vue { @ProvideReactive(s) private bar = 'deeper lorry' } </script>
复制代码
子组件:
<script> @Component export default class InjectComponent extends Vue { @InjectReactive(s) private baz!: string } </script>
复制代码
以上为文档中罗列的用法,之后项目过程当中遇到了别的会回来更新.
敬请指正.