时间:2021-05-18
忙里偷闲,整理了一下关于如何借助 vue-cli3 搭建 ts + 装饰器 的脚手架,并如何自定义 webpack 配置,优化。
准备工作
安装 node
安装 vue-cli3
参考官方文档:https://cli.vuejs.org/zh/guide/
补充
npm list -g --depth 0 // 查看全局安装的包npm outdated -g --depth=0 // 查看需要更新的全局包npm update 包名 -g // 更新全局安装的包搭建项目
可参考:使用Vue-cli 3.0搭建Vue项目
新建一个基于 ts 的 vue 项目
备注:如果是 window 系统,用 git bash 交互提示符(切换)不会工作,用以下命令,即可解决:
winpty vue.cmd create vue-cli3-ts交互说明:
在已存在的项目中添加 ts
会把所有 .js 更改为 .ts
script 命令
npm run serve 启动服务:http://localhost:8080/#/
vue 中 ts 语法
demo: src/components/HelloWorld.vue
<script lang="ts">import { Component, Prop, Vue } from 'vue-property-decorator';@Componentexport default class HelloWorld extends Vue { @Prop() private msg!: string;}</script>和普通的 vue 项目不一样的就是.vue 文件中 script 的 写法。
主要用到的一个库:vue-property-decorator
用法可参考:
1. 类型注解,类型推论
错误时,vscode 编辑器会有红色波浪号提示。
数组
let names: string[]; // Array<string>names = ['Tom'];任意类型,没有类型限制
let foo: any;foo = 'foo';foo = 3;let list: any[];list = [1, true, 'free'];list[1] = 100;函数中使用类型
function greeting (person: string): string { return 'Hello, ' + person;}// void 类型,常用于没有返回值的函数function warnUser (): void { alert('This is msg');}案例:vue demo
<template> <div class="hello"> <input type="text" placeholder="请输入新特性" @keyup.enter="addFeature" /> <ul> <li v-for="feature in features" :key="feature">{{feature}}</li> </ul> </div></template><script lang="ts">import { Component, Prop, Vue } from 'vue-property-decorator';@Componentexport default class Demo extends Vue { // 相当于 data 中的数据项 features: string[]; constructor () { super(); this.features = ['类型注解', '类型推论', '编译型语言']; } // 相当于 methods 中的方法 addFeature (event: any) { console.log(event); this.features.push(event.target.value); event.target.value = ''; }}</script>2.类
ts 中的类和 es6 中的大体相同,关注特性 访问修饰符
构造函数:初始化成员变量,参数加上修饰符,能够定义并初始化一个属性
constructor (private name = 'Tom') { super();}等同于
name: string;constructor () { super(); this.name = 'Tom';}存取器,暴露存取数据时可添加额外逻辑;在 vue 中可用作计算属性
get fullName () { return this.name; }set fullName (val) { this.name = val; }案例:vue demo
<template> <p>特性数量:{{count}}</p></template><script lang="ts"> export default class Demo extends Vue { // 定义 getter 作为计算属性 get count () { return this.features.length; } }</script>接口
接口仅约束结构,不要求实现
interface Person { firstName: string; lastName: string;}function greeting (person: Person) { return `Hello, ${person.firstName} ${person.lastName}`;}const user = {firstName: 'Jane', lastName: 'user'};console.log(greeting(user));案例:vue demo,声明接口类型约束数据结构
<template> <li v-for="feature in features" :key="feature.id">{{feature.name}}</li></template><script lang="ts"> // 定义一个接口约束feature的数据结构 interface Feature { id: number; name: string; } export default class Demo extends Vue { private features: Feature[]; constructor () { super(); this.features = [ {id: 1, name: '类型注解'}, {id: 2, name: '类型推论'}, {id: 3, name: '编译型语言'} ] } }</script>泛型
泛型 是指在定义函数、接口或类的时候,不预先指定具体的类,而是在使用时才指定类型的一种特性。
interface Result<T> { data: T;}// 不使用泛型interface Result { data: Feature[];}案例:使用泛型约束接口返回类型
function getData<T>(): Result<T> { const data: any = [ {id: 1, name: '类型注解'}, {id: 2, name: '类型推论'}, {id: 3, name: '编译型语言'} ]; return {data};}// 调用this.features = getData<Feature[]>().data;案例:使用泛型约束接口返回类型 Promise
function getData<T>(): Promise<Result<T>> { const data: any = [ {id: 1, name: '类型注解'}, {id: 2, name: '类型推论'}, {id: 3, name: '编译型语言'} ]; return Promise.resolve<Result<T>>({data});}// 调用 async 方式async mounted () { this.features = (await getData<Feature[]>()).data;}// 调用 then 方式mouted () { getData<Feature[]>().then((res: Result<Feature[]>) => { this.features = res.data; })}装饰器
装饰器用于扩展类或者它的属性和方法。
属性声明:@Prop
除了在 @Component 中声明,还可以采用@Prop的方式声明组件属性
export default class Demo extends Vue { // Props() 参数是为 vue 提供属性选项 // !称为明确赋值断言,它是提供给ts的 @Prop({type: String, require: true}) private msg!: string;}事件处理:@Emit
// 通知父类新增事件,若未指定事件名则函数名作为事件名(驼峰变中划线分隔)@Emit()private addFeature(event: any) {// 若没有返回值形参将作为事件参数 const feature = { name: event.target.value, id: this.features.length + 1 }; this.features.push(feature); event.target.value = ""; return feature;// 若有返回值则返回值作为事件参数}template 模板组件上正常写,@add-feature
变更监测:@Watch
@Watch('msg')onRouteChange(val:string, oldVal:any){ console.log(val, oldVal);}装饰器原理
装饰器本质是工厂函数,修改传入的类、方法、属性等
类装饰器
// 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。function log(target: Function) { // target是构造函数 console.log(target === Foo); // true target.prototype.log = function() { console.log(this.bar);}// 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。}@logclass Foo { bar = 'bar'}const foo = new Foo();// @ts-ignorefoo.log();实战一下 Component,新建 Decor.vue
<template> <div>{{msg}}</div></template><script lang='ts'> import { Vue } from "vue-property-decorator"; function Component(options: any) { return function(target: any) { return Vue.extend(options); }; } @Component({ props: { msg: { type: String, default: "" } } }) export default class Decor extends Vue {}</script>源码简单了解
类装饰器主要依赖库:vue-class-component,深入源码,了解其背后究竟做了什么。
vue-property-decorator.js
import Vue from 'vue';import Component, { createDecorator, mixins } from 'vue-class-component';export { Component, Vue, mixins as Mixins };createDecorator、applyMetadata 是核心,后续实现都依赖它,比如 Prop、Watch、Ref。
Prop 源码实现:
export function Prop(options) { if (options === void 0) { options = {}; } return function (target, key) { applyMetadata(options, target, key); createDecorator(function (componentOptions, k) { ; (componentOptions.props || (componentOptions.props = {}))[k] = options; })(target, key); };}applyMetadata,见名知义,就是将装饰器中的信息拿出来放到 options.type 中。
/** @see {@link https://github.com/vuejs/vue-class-component/blob/master/src/reflect.ts} */var reflectMetadataIsSupported = typeof Reflect !== 'undefined' && typeof Reflect.getMetadata !== 'undefined';function applyMetadata(options, target, key) { if (reflectMetadataIsSupported) { if (!Array.isArray(options) && typeof options !== 'function' && typeof options.type === 'undefined') { options.type = Reflect.getMetadata('design:type', target, key); } }}Reflect.getMetadata 获取设置在类装饰器上的元数据。可参考文章理解:
createDecorator,见名知义,就是创建装饰器。本质是在类上定义一个私有属性
export function createDecorator(factory) { return function (target, key, index) { var Ctor = typeof target === 'function' ? target : target.constructor; if (!Ctor.__decorators__) { Ctor.__decorators__ = []; } if (typeof index !== 'number') { index = undefined; } Ctor.__decorators__.push(function (options) { return factory(options, key, index); }); };}项目代理及 webpack 性能优化
在项目根目录下新建 vue.config.js
本地开发 api 代理
本地开发 api 模拟
性能优化
查看打包依赖
在 package.json 文件 script 中加入命令:
"build:report": "vue-cli-service build --report"会在 dist 目录下生成 report.html,可直接打开,查看打包依赖,进行分析,进行打包优化
打包优化 - cdn 引入公共库
在 vue.config.js 中加入配置:
configureWebpack: { externals: { // cdn 外链,避免包太大,首屏优化 'vue': 'Vue', 'vue-router': 'VueRouter', 'vuex': 'Vuex' }}在 public/index.html 中加入 cdn 库地址
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js"></script><!-- built files will be auto injected -->再次优化,html head 信息中加,dns 域名预解析,js 库 reload 预加载。
<link rel="dns-prefetch" href="cdnjs.cloudflare.com" rel="external nofollow" ><link href="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js" rel="preload" as="script"><link href="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js" rel="preload" as="script"><link href="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js" rel="preload" as="script">其他
修改本地开发端口号,在 vue.config.js 中加入配置:
devServer: { port: 8888}体验优化-打包完成提示:
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');const path = require('path');module.exports = { // 链式操作 chainWebpack: config => { // 移除 prefetch 插件,移动端对带宽敏感 // 路由懒加载,只对用户频繁操作的路由,通过 注释 提前获取 // component: () => import( '../views/About.vue') config.plugins.delete('prefetch'); // 生产打包才提示,开发不提示 if (process.env.NODE_ENV === 'production') { config.plugin('build-notify').use(WebpackBuildNotifierPlugin, [{ title: "My Project Webpack Build", logo: path.resolve("./img/favicon.png"), suppressSuccess: true }]) } }}以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
前言假设已经有一个通过vue-cli3脚手架构建的vue项目命令行安装Typescriptnpminstall--save-devtypescriptnpmin
一、什么是脚手架?1.VueCLIVueCLI是一个基于Vue.js进行快速开发的完整系统,提供:通过@vue/cli实现的交互式的项目脚手架。通过@vue/c
安装vue-cli3环境准备1.如果您已安装vue-cli2,请先删除当前脚手架,否则无法成功安装vue-cli3。npmuninstallvue-cli-g2
问题现象项目使用vue/cli3脚手架搭建的前端项目,vue版本为2.6.10。browserslist的配置如下:[">1%","last2versions"
用vue-cli脚手架可以快速的构建出一个前端vue框架的项目结构。今天小编我便来记录一下vue-cli脚手架的构建项目的经验。1、首先便是要搭建好vue-cl