您好,登錄后才能下訂單哦!
忙里偷閑,整理了一下關于如何借助 vue-cli3 搭建 ts + 裝飾器 的腳手架,并如何自定義 webpack 配置,優化。
準備工作
安裝 node
npm i nrm -g // 安裝 nrm ls // 查看可用源,及當前源,帶*的是當前使用的源 nrm use taobao // 切換源,使用源 nrm add <registry> <url> // 其中reigstry為源名,url為源的路徑 nrm del <registry> // 刪除對應的源 nrm test npm // 測試源的響應速度
安裝 vue-cli3
參考官方文檔:https://cli.vuejs.org/zh/guide/
npm i @vue/cli -g // 全局安裝 vue --version // 檢查是否安裝
補充
npm list -g --depth 0 // 查看全局安裝的包 npm outdated -g --depth=0 // 查看需要更新的全局包 npm update 包名 -g // 更新全局安裝的包
搭建項目
可參考:使用Vue-cli 3.0搭建Vue項目
新建一個基于 ts 的 vue 項目
vue create vue-cli3-ts
備注:如果是 window 系統,用 git bash 交互提示符(切換)不會工作,用以下命令,即可解決:
winpty vue.cmd create vue-cli3-ts
交互說明:
在已存在的項目中添加 ts
vue add @vue/typescript
會把所有 .js 更改為 .ts
script 命令
// - 啟動服務 npm run serve // - 打包編譯 npm run build // - 執行lint npm run lint // - 執行單元測試 npm run test:unit
npm run serve 啟動服務:http://localhost:8080/#/
vue 中 ts 語法
demo: src/components/HelloWorld.vue
<script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; } </script>
和普通的 vue 項目不一樣的就是.vue 文件中 script 的 寫法。
主要用到的一個庫:vue-property-decorator
用法可參考:
1. 類型注解,類型推論
let title: string; // 類型注解 title = 'ts'; // 正確 title = 4; // 錯誤 let text = 'txt'; // 類型推論 text = 2; // 錯誤
錯誤時,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'; @Component export 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); } // 如果類裝飾器返回一個值,它會使用提供的構造函數來替換類的聲明。 } @log class Foo { bar = 'bar' } const foo = new Foo(); // @ts-ignore foo.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 代理
module.exports = { devServer: { proxy: { '/api': { target: '<url>', changeOrigin: true, pathRewrite: { '^/api': '' } } } } }
本地開發 api 模擬
devServer: { before (app) { before (app) { app.get('/api/getList', (req, res) => { res.json({data: [{id: 1, name: 'vue'}]}) }) } } }
性能優化
查看打包依賴
在 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 rel="preload" as="script"> <link rel="preload" as="script"> <link 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(/* webpackChunkName: "about" */ /* webpackPrefetch: true */'../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 }]) } } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。