vue-class-component 是Vue英文官网推荐的一个包,可以以class的模式写vue组件,它带来了很多便利:
methods,钩子都可以直接写作class的方法
computed属性可以直接通过get来获得
初始化data可以声明为class的属性
其他的都可以放到Component装饰器里
vue-property-decorator
vue-property-decorator 这个包完全依赖于vue-class-component,提供了多个装饰器,辅助完成prop、watch、model等属性的声明。
编译准备
由于使用的是装饰器语法糖,我们需要在我们webpack的babel编译器中对齐进行支持。
首先是class语法支持,针对babel6及更低的版本,需要配置babel的plugin中添加class语法支持插件babel-plugin-transform-class-properties,针对babel7,需要使用插件@babel/plugin-proposal-class-properties对class进行语法转换。
然后是装饰器语法支持,针对babel6及更低的版本,需要配置babel的plugin中添加装饰器语法支持插件babel-plugin-transform-decorators-legacy,针对babel7,需要使用插件@babel/plugin-proposal-decorators对装饰器进行语法转换。
针对bable6,配置.babelrc如下
{ "presets": ["env", "stage-1"], "plugins": [ "transform-runtime", "syntax-dynamic-import", "transform-class-properties", // 新增class语法支持 "transform-decorators-legacy" // 新增装饰器语法支持 ] }
对于bable7,官方推荐直接使用@vue/apppreset,该预设包含了@babel/plugin-proposal-class-properties和@babel/plugin-proposal-decorators两个插件,另外还包含了动态分割加载chunks支持@babel/plugin-syntax-dynamic-import,同时也包含了@babel/envpreset,.babelrc配置如下:
{ "presets": [ ["@vue/app", { "loose": true, "decoratorsLegacy": true }] ] }
重写组件
编译插件准备好之后,我们对上面的Vue组件进行改写,代码如下
<template> <div> <h1>{{title}}</h1> <span>{{text}}<span> <button @click="setText">按钮</button> </div> </template> <script> import { Vue, Component, Watch, Prop } from 'vue-property-decorator'; @Component export default class Demo extends Vue { text = 'hello world'; @Prop({type: String, default: 'Vue Demo'}) title; @Watch('title') titleChange(newTitle, oldTitle) { console.log(`标题从 ${oldTile} 变为了 ${newTitle}`) } setText(e) { this.text = '点击了按钮'; } } </script>
到此为止,我们的组件改写完毕,相对先前的“写配置”的写法,看起来相对来说要好理解一些吧。
注意:Vue的class的写法的methods还是没办法使用箭头函数进行的,详细原因这里就不展开,大概就是因为Vue内部挂载函数的方式的原因。
视图开发
特性对比
针对视图的开发,Vue推崇html、js、css分离的写法,React推崇all-in-js,所有都在js中进行写法。
当然各有各的好处,如Vue将其进行分离,代码易读性较好,但是在html中无法完美的展示JavaScript的编程能力,而对于React的jsx写法,因为有JavaScript的编程语法支持,让我们更灵活的完成视图开发。
对于这类不灵活的情况,Vue也对jsx进行了支持,只需要在babel中添加插件babel-plugin-transform-vue-jsx、babel-plugin-syntax-jsx、babel-helper-vue-jsx-merge-props(babel6,对于babel7,官方推荐的@vue/app预设中已包含了jsx的转化插件),我们就可以像React一样,在组件中声明render函数并返回jsx对象,如下我们对上一节的组件进行改造:
组件改造
<script> import { Vue, Component, Watch, Prop } from 'vue-property-decorator'; @Component export default class Demo extends Vue { title = 'hello world'; @Prop({type: String, default: 'Vue Demo'}) title; @Watch('title') titleChange(newTitle, oldTitle) { console.log(`标题从 ${oldTile} 变为了 ${newTitle}`) } setText(e) { this.text = '点击了按钮'; } render() { const { title, text } = this; return <div> <h1>{title}</h1> <span>{text}<span> <button onClick={this.setText}>按钮<button> </div> } } </script>
Vue的jsx使用注意点
写到这里,也基本上发现其写法已经与React的class写法雷同了。那么Vue的jsx和React的jsx有什么不同呢。
在React的jsx语法需要React支持,也就是说,在你使用jsx的模块中,必须引进React。
而Vue的jsx语法需要Vue的createElement支持,也就是说在你的jsx语法的作用域当中,必须存在变量h,变量h为createElement的别名,这是Vue生态系统中的一个通用惯例,在render中h变量由编译器自动注入到作用域中,自动注入详情见plugin-transform-vue-jsx,如果没有变量h,需要从组件中获取并声明,代码如下:
const h = this.$createElement;
这里借助官方的一个例子,基本包含了所有Vue的jsx常用语法,如下: