0
点赞
收藏
分享

微信扫一扫

学习搭建Vue的TypeScript环境

Python百事通 2021-09-24 阅读 61

一、配置 webpack

首先通过:

npm init

来创建依赖文件:package.json

接下来安装 webpack,因为 webpack 属于开发环境下的依赖,所以安装在 devDependencies 里面。

npm install -D webpack webpack-cli webpack-dev-server

接下来整理和创建文件,文件结构为:


├─ package.json
│  webpack.config.js
│  
├─public
│      index.html
│      
└─src
        main.js

先说下怎么获取这个文档树的,windows下打开 CMD:

tree /f > filetree.txt

tree 只能获取到文件夹,/f 能获取到具体文件,然后通过 > filetree.txt 写入文件。如果有 node_modules 的影响,我们可以忽略这个文件夹,这时的借助 git 来实现:

-I命令允许你使用正则匹配来排除掉你不想看到的文件夹
tree -I "node_modules"
也可以使用|同时排除掉多个文件夹:
tree -I "node_modules|cache|test_*"
有时候文件夹层级很深,我们只关心前两级的文件,可以使用如下-L :
tree -L 2

这时来看看各个文件的内容:
webpack.config.js:

module.exports = {
    
    /* 
        mode:"production" | "development" | "none" 
        production:enable many optimizations for production builds
        development:enabled useful tools for development
        none: no defaults,没有默认,必须设置为production或development
        Chosen mode tells webpack to use its built-in optimizations accordingly
    */
    //--mode=development会将 process.env.NODE_ENV 的值设为 development。并启动相应的插件
    mode:"development",

    /* 基础目录,用于从配置中解析入口起点(entry point)和 loader */
    // context: path.resolve(__dirname, "app")

    /* 应用程序的起点入口。从这个起点开始,应用程序启动执行。
    */
    entry:"./src/main.js",

    output:{
        filename:"bundle.js",
        // 
        publicPath:"virtual"
    },
    module:{
        rules:[

        ]
    },
    resolve:{
        extensions:[".js",".vue",".css",".json"]
    },
    devServer:{
        open: true,
        /* When open is enabled, the dev server will open the browser.
        使用默认浏览器打开服务
        open: true
        Usage via the CLI//命令行中使用
        webpack-dev-server --open
        If no browser is provided (as shown above), your default browser will be used. To specify a different browser, just pass its name:
        //指定浏览器
        webpack-dev-server --open 'Google Chrome */


        /* 指定打开浏览器时要浏览的页面。
        Specify a page to navigate to when opening the browser. 
        默认浏览的页面是webpack.config.js所在目录的index.html文件
        */
        openPage: './public/index.html'
    },
    plugins:[

    ]
}

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue中使用TypeScript</title>
</head>
<body>
    <script async src="../virtual/bundle.js"></script>
</body>
</html>

main.js:

alert(process.env.NODE_ENV);//development

欧克了,现在来启动服务:

启动服务, --progress可以将运行进度输出到控制台。
npx webpack-dev-server --progress

二、启动 JS 版 Vue

安装 Vue:

npm i -S vue

在 index.html 增加一个一行:

<div id="app"></div>

这时更改main.js文件:

import Vue from "vue";

new Vue({
    el:"#app",
    render:function(h){
        return h("h1","TypeScript!")
    }
})

打开网址查看,没有问题。但是如果把 main.js 改成这样:

import Vue from "vue";

new Vue({
    el:"#app",
    data:{
        name: "TypeScript!"
    },
    template:"<h1>{{name}}</h1>"
})

会看到浏览器报错:

vue.runtime.esm.js:620 [Vue warn]: You are using the runtime-only build of Vue where the
template compiler is not available. Either pre-compile the templates into render functions,
or use the compiler-included build.
[Vue警告]:您正在使用Vue的仅运行时版本,而模板编译器不可用。可以将模板预编译为渲染函数,
也可以使用包含编译器的内部版本。

Vue 官网演示案例:

https://cn.vuejs.org/v2/guide/installation.html#%E6%9C%AF%E8%AF%AD
// 需要编译器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要编译器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

如果你使用 render(h) 渲染函数就不需要去 webpack 指定完整版了。现在我们去指定下 Vue 的版本,修改 webpack.config.js,需要注意的是:因为运行时版本相比完整版体积要小大约30%,所以应该尽可能使用这个版本。如果你仍然希望使用完整版,则需要在打包工具里配置一个别名:

module.exports = {
  // ...
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
    }
  }
}
//https://cn.vuejs.org/v2/guide/installation.html

这个时候在 main.js 同级目录下创建 App.vue 文件,同时更改 main.js 文件的内容为:

import Vue from "vue";
import App from "./App";

new Vue({
    el:"#app",
    render:h=>h(App)
});

不过这时候我们还不能使用单文件组件,需要 loader 对应的 loader:
安装依赖:

npm i -D vue-loader vue-template-compiler

注意:

去 webpack.config.js 配置下loader:

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  module: {
    rules: [
      // ... 其它规则
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}

你肯定好奇,vue-Loader 的配置和其它的 loader 怎么不太一样。除了通过一条规则将 vue-loader 应用到所有扩展名为 .vue 的文件上之外,还在 plugins 里使用了 VueLoaderPlugin 插件。这个插件是干啥的?

现在我们还没有安装处理 css 的 loader,这时我们对 App.vue 写上样式就会报错, App.vue 的内容:

<template>
    <h1>哈哈</h1>
</template>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

安装处理样式的依赖:

npm i -D css-loader vue-style-loader

在去配置下 webpack :

module:{
    rules:[
        {
            test: /\.vue$/,
            loader: 'vue-loader'
        },
        {
            test: /\.css$/,
            use: [
                //顺序不能倒过来
                'vue-style-loader',
                'css-loader'
            ]
        }
    ]
},

完成显示:

三、启动 TS 版 Vue

Vue 时自带 xx.d.ts 声明文件的,所以不用我们安装支持 TS 的第三方插件。

接着配置,安装插件并生成 ts.config.json

npm i -D ts-loader typescript
npx tsc --init

修改 webpack.config.js 的配置:

{ 
    test: /\.ts?$/, 
    loader: "ts-loader",
    options: {
        // .vue文件必须加
        appendTsSuffixTo: [/\.vue$/]
    },
    exclude:/node_modules/
}

App.vue 文件内容改为:

<template>
    <h1>{{name}}</h1>
</template>
<script lang="ts">
//也执行tsx,也就是jsx语法
import Vue from "vue";

export default Vue.extend({
    data(){
        return {
            name:"TypeScript!"
        }
    }
})
</script>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>
//下面这种写法也行
<template>
    <h1>{{name}}</h1>
</template>
<script lang="ts">
export default {
    data(){
        return {
            name:123
        }
    }
}
</script>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

main.js 改为 main.ts 内容改成:

import Vue from "vue";
// 不能不加.vue后缀,不加会报错
import App from "./App.vue";

new Vue({
    el:"#app",
    render:h=>h(App)
});

这时启动服务会有个问题:

Cannot find module './App.vue'.

不能找到模块,由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shims.d.ts 文件,意思是告诉 TypeScript 以 *.vue 为后缀的文件可以交给 vue 模块来处理 (真正的原因是导入的模块需要一个 Vueconstructor)

而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。原因是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件。
在 src 目录下新建 vue-shims.d.ts:

declare module '*.vue' {
    import Vue from 'vue';
    export default Vue;
}

四、类组件

上面单文件组件 Vue 的写法固然是没问题,但是不能很好的去使用 ES6 类等语法,所以我们要改成类组件的形式。

安装依赖:

npm i -D vue-class-component

然后去修改下 App.vue 文件:

<template>
    <div class="App">
        <h1>{{count}}</h1>
        <h3>{{name}}</h3>
        <button @click="increment">加加</button>
        <button @click="decrement">减减</button>
        <h5>计算属性{{computed}}</h5>
        <button @click="computed++">设置计算属性</button>
    </div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component from 'vue-class-component';

// Define the component in class-style
//@Component 装饰器使您的类成为Vue组件:
@Component({
    // 对于所有其他选项,请将它们传递给装饰器函数:
    // 如果没有可以省略
    props:["name"],
    components:{

    }
})
export default class App extends Vue {
    // Class properties will be component data
    //类属性就是data中return 的数据
    private count:number = 0;

    // Methods will be component methods
    //组件methods可以直接声明为类原型方法
    private increment():void {
        this.count++
    }

    private decrement():void {
        this.count--
    }

    // 生命周期
    private mounted():void{
        console.log(this.count);
    }

    // 计算属性声明为类属性getter / setter:
    get computed():number{
        return this.count ** 2 ;
    }

    //  A 'set' accessor cannot have a return type annotation.
    // set访问器不能设置返回类型
    set computed(value:number){
        this.count = value++;
        console.log(value);
    }
}
</script>
<style scoped>
.App{
    margin: 100px auto;
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 300px;
    height: 400px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

注意 main.ts 也被修改了:

import Vue from "vue";
// 不能不加.vue,会报错
import App from "./App.vue";

new Vue({
    el:"#app",
    components:{
        App
    },
    template:"<App name='TypeScript' />"
});

又因为我们用到了装饰器,所以的更改下 tsconfig.js 文件,打开装饰器语法:

"experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */

演示效果:

更多官网 API :https://class-component.vuejs.org/

五、Vue 装饰器

上面的案例我们使用了一个装饰器,挺好用的是不是,但是可以只有一个,如果想要更多可以参考官网自定义装饰器来给我们使用,我们当然不使用这种方式,我们使用插件:

npm i -D vue-property-decorator

看看官网提供了多少个装饰器:

There are several decorators and 1 function (Mixin):

@Prop
@PropSync
@Model
@Watch
@Provide
@Inject
@ProvideReactive
@InjectReactive
@Emit
@Ref
@Component (provided by vue-class-component)
Mixins (the helper function named mixins provided by vue-class-component)

注意这个插件是依赖vue-class-component的。

我们来看几个最常用的,现在 App.vue 是 Children.vue 的父亲,main.ts 内容不变,来开始吧:
App.vue

<template>
    <div class="App">
        <h1>{{count}}</h1>
        <h3>{{name}}</h3>
        <Children @info="receive" @returnValue="returnValue"/>
    </div>
</template>

<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import Children from './Children.vue';

// Define the component in class-style
//@Component 装饰器使您的类成为Vue组件:
@Component({
    components:{
        Children
    },
    watch:{
        /* count(value){
            console.log(value);
        } */
        count:{
            handler:(value , oldValue)=>{
                console.log(value,oldValue);
            },
            immediate:true,
            deep: true
        }
    }
})
export default class App extends Vue {
    
    // String开头字母大写
    @Prop(String) name:string | undefined;
    private count:number = 0;
    
    
    private receive(val:number):void {
        this.count = val;
    }
    private returnValue(val:number):void {
        this.count = val;
    }

    @Watch("count" , { immediate: true, deep: true })
    watch_count(val:any, oldVal:any) {
        console.log(val,oldVal);
    }

}
</script>
<style scoped>
.App{
    margin: 100px auto;
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 300px;
    height: 400px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

Children.vue

<template>
    <div class="chilren">
        <p>我是子组件children</p>
        <button @click="send">点我发射</button>
        <button @click="returnValue">click me send</button>
    </div>
</template>

<script lang="ts">
import { Vue, Component, Emit } from 'vue-property-decorator';

@Component
export default class Chilren extends Vue {
    private val:number = 0;

    @Emit("returnValue")
    returnValue() {
        this.val++;
        return this.val;
    }

    private send():void{
        this.val++;
        this.$emit("info",this.val);
    }
}
</script>

演示效果:

更多 API 参考官网:https://www.npmjs.com/package/vue-property-decorator

六、提升编译速度

现在项目编译一次需要时间为:

Time: 6236ms

随着项目变得越来越大,编译时间线性增加。这是因为在每次重建时,类型脚本的语义检查器必须检查所有文件。简单的解决方案是通过使用transfileOnly:true选项来禁用它,但是这样做会使您没有类型检查,并且不会输出声明文件。

所以我们配置下 webpack.config.js 文件:

{
    test: /\.tsx?$/,
    use: [
        {
            loader: 'ts-loader',
            options: {
                transpileOnly: true
            }
        }
    ]
}

再次编译用时:

Time: 1659ms

快了不是一星半点。但是现在类型检查没了,这则么能够,这时我们使用 fork-ts-checker-webpack-plugin 另外开一个线程来进行类型检查,安装依赖:

npm install -D fork-ts-checker-webpack-plugin
//使用:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
plugins:[
    new ForkTsCheckerWebpackPlugin()
]

再次编译用时:

//无类型检查编译用时
Time: 1947ms
//加上类型检查用时
Time: 3284ms

就这也快了一半。

2020年2月15日 01点05分

举报

相关推荐

0 条评论