zoukankan      html  css  js  c++  java
  • 初探webpack之从零搭建Vue开发环境

    初探webpack之搭建Vue开发环境

    平时我们可以用vue-cli很方便地搭建Vue的开发环境,vue-cli确实是个好东西,让我们不需要关心webpack等一些繁杂的配置,然后直接开始写业务代码,但这会造成我们过度依赖vue-cli,忽视了webpack的重要性,当遇到一些特殊场景时候,例如Vue多入口的配置、优化项目的打包速度等等时可能会无从下手。当然现在才开始学习vue2 + webpack可能有点晚,毕竟现在都在考虑转移到vue3 + vite了哈哈。

    描述

    文中相关的代码都在https://github.com/WindrunnerMax/webpack-simple-environment中的webpack--vue-cli分支中。webpack默认情况下只支持jsjson格式的文件,所以要把cssimghtmlvue等等这些文件转换成js,这样webpack才能识别,而实际上搭建Vue的开发环境,我们的主要目的是处理.vue单文件组件,最主要的其实就是需要相对应的loader解析器,主要工作其实就在这里了,其他的都是常规问题了。

    实现

    搭建环境

    初探webpack,那么便从搭建简单的webpack环境开始,首先是初始化并安装依赖。

    $ yarn init -y
    $ yarn add -D webpack webpack-cli cross-env
    

    首先可以尝试一下webpack打包程序,webpack可以零配置进行打包,目录结构如下:

    webpack-simple
    ├── package.json
    ├── src
    │   ├── index.js
    │   └── sum.js
    └── yarn.lock
    
    // src/sum.js
    export const add = (a, b) => a + b;
    
    // src/index.js
    import { add } from "./sum";
    console.log(add(1, 1));
    

    之后写入一个打包的命令。

    // package.json
    {
        // ...
        "scripts": {
            "build": "webpack"
        },
        // ...
    }
    

    执行npm run build,默认会调用node_modules/.bin下的webpack命令,内部会调用webpack-cli解析用户参数进行打包,默认会以src/index.js作为入口文件。

    $ npm run build
    

    执行完成后,会出现警告,这里还提示我们默认modeproduction,此时可以看到出现了dist文件夹,此目录为最终打包出的结果,并且内部存在一个main.js,其中webpack会进行一些语法分析与优化,可以看到打包完成的结构是。

    // src/main.js
    (()=>{"use strict";console.log(2)})();
    

    webpack配置文件

    当然我们打包时一般不会采用零配置,此时我们就首先新建一个文件webpack.config.js。既然webpack说默认modeproduction,那就先进行一下配置解决这个问题,因为只是一个简单的webpack环境我们就不区分webpack.dev.jswebpack.prod.js进行配置了,简单的使用process.env.NODE_ENVwebpack.config.js中区分一下即可,cross-env是用以配置环境变量的插件。

    // package.json
    {
        // ...
        "scripts": {
            "build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
        },
        // ...
    }
    
    const path = require("path");
    module.exports = {
        mode: process.env.NODE_ENV,
        entry: "./src/index.js",
        output: {
            filename: "index.js",
            path:path.resolve(__dirname, "dist")
        }
    }
    

    HtmlWebpackPlugin插件

    我们不光是需要处理js文件的,还需要处理html文件,这里就需要使用html-webpack-plugin插件。

    $ yarn add -D html-webpack-plugin
    

    之后在webpack.config.js中进行配置,简单配置一下相关的输入输出和压缩信息,另外如果要是想每次打包删除dist文件夹的话可以考虑使用clean-webpack-plugin插件。

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
        mode: process.env.NODE_ENV,
        entry: "./src/index.js",
        output: {
            filename: "index.js",
            path:path.resolve(__dirname, "dist")
        },
        plugins:[
            new HtmlWebpackPlugin({
                title: "Webpack Template", 
                filename: "index.html", // 打包出来的文件名 根路径是`module.exports.output.path`
                template: path.resolve("./public/index.html"),
                hash: true, // 在引用资源的后面增加`hash`戳
                minify: {
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    minifyCSS: true,
                    minifyJS: true,
                },
                inject: "body", // `head`、`body`、`true`、`false`
                scriptLoading: "blocking" // `blocking`、`defer`
            })
        ]
    }
    

    之后新建/public/index.html文件,输入将要被注入的html代码。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title><%= htmlWebpackPlugin.options.title %></title>
      </head>
      <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    

    启动npm run build,我们就可以在/dist/index.html文件中看到注入成功的代码了。

    <!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>Webpack Template</title></head><body><div id=app></div><!-- built files will be auto injected --><script src=index.js?94210d2fc63940b37c8d></script></body></html>
    

    webpack-dev-server

    平时开发项目,预览效果时,一般直接访问某个ip 和端口进行调试的,webpack-dev-server就是用来帮我们实现这个功能,他实际上是基于express来实现web服务器的功能,另外webpack-dev-server打包之后的htmlbundle.js是放在内存中的,目录里是看不到的,一般会配合webpack的热更新来使用。

    $ yarn add -D webpack-dev-server
    

    接下来要在webpack.config.js配置devServer环境,包括package.json的配置。

    // webpack.config.js
    
    // ...
    module.exports = {
        // ...
        devServer: {
            hot: true, // 开启热更新
            open: true, // 自动打开浏览器预览
            compress: true, // 开启gzip
            port: 3000  //不指定端口会自动分配
        },
        // ...
    }
    
    // package.json
    // ...
    "scripts": {
        "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
        "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
    },
    // ...
    

    随后运行npm run dev即可自动打开浏览器显示预览,当然按照上边的代码来看页面是空的,但是可以打开控制台发现确实加载了DOM结构,并且Console中显示了我们console2,并且此时如果修改了源代码文件,比如在DOM中加入一定的结构,发现webpack是可以进行HMR的。

    搭建Vue基础环境

    首先我们可以尝试一下对于.js中编写的Vue组件进行构建,即不考虑单文件组件.vue文件的加载,只是构建一个Vue对象的实例,为了保持演示的代码尽量完整,此时我们在src下建立一个main.js出来作为之后编写代码的主入口,当然我们还需要在index.js中引入main.js,也就是说此时代码的名义上的入口是main.js并且代码也是在此处编写,实际对于webpack来说入口是index.js,截至此时的commit625814a
    首先我们需要安装Vue,之后才能使用Vue进行开发。

    $ yarn add vue
    

    之后在/src/main.js中编写如下内容。

    // /src/main.js
    import Vue from "vue";
    
    new Vue({
        el: "#app",
        template: "<div>Vue Example</div>"
    })
    

    另外要注意这里需要在webpack.config.js中加入如下配置,当然这里只是为了处理Vuecompiler模式,而默认是runtime模式, 即引入指向dist/vue.runtime.common.js,之后我们处理单文件组件.vue文件之后,就不需要这个修改了,此时我们重新运行npm run dev,就可以看到效果了。

    // webpack.config.js
    
    // ...
    module.exports = {
        // ...
        resolve: {
            alias: {
              "vue$": "vue/dist/vue.esm.js" 
            }
        },
        // ...
    }
    

    之后我们正式开始处理.vue文件,首先新建一个App.vue文件在根目录,此时的目录结构如下所示。

    webpack-simple-environment
    ├── dist
    │   ├── index.html
    │   └── index.js
    ├── public
    │   └── index.html
    ├── src
    │   ├── App.vue
    │   ├── index.js
    │   ├── main.js
    │   └── sum.js
    ├── jsconfig.js
    ├── LICENSE
    ├── package.json
    ├── README.md
    ├── webpack.config.js
    └── yarn.lock
    

    之后我们修改一下main.js以及App.vue这两个文件。

    import Vue from "vue";
    import App from "./App.vue";
    
    const app = new Vue({
        ...App,
    });
    app.$mount("#app");
    
    <!-- App.vue -->
    <template>
        <div class="example">{{ msg }}</div>
    </template>
    
    <script>
    export default {
        name: "App",
        data: () => ({
            msg: "Example"
        })
    }
    </script>
    
    <style scoped>
        .example{
            font-size: 30px;
        }
    </style>
    

    之后便是使用loader进行处理的环节了,因为我们此时需要对.vue文件进行处理,我们需要使用loader处理他们。

    $ yarn add -D vue-loader vue-template-compiler css-loader vue-style-loader
    

    之后需要在webpack.config.js中编写相关配置,之后我们运行npm run dev就能够成功运行了,此时的commit id831d99d

    // webpack.config.js
    
    // ...
    const VueLoaderPlugin = require("vue-loader/lib/plugin")
    
    module.exports = {
        // ...
        module: {
            rules: [
                {
                    test: /.vue$/,
                    use: "vue-loader",
                },
                {
                    test: /.css$/,
                    use: [
                        "vue-style-loader",
                        "css-loader"
                    ],
                },
            ],
        },
        plugins:[
            new VueLoaderPlugin(),
            // ...
        ]
    }
    

    处理资源文件

    通常我们需要处理资源文件,同样是需要使用loader进行处理,主要是对于图片进行处理,搭建资源文件处理完成后的commit idf531cc1

    $ yarn add -D url-loader file-loader
    
    // webpack.config.js
    
    // ...
    module.exports = {
        // ...
        module: {
            rules: [
                // ...
                {
                    test: /.(png|jpg|gif)$/i,
                    use: [
                        {
                            loader: "url-loader",
                            options: {
                                esModule: false,
                                limit: 8192, //小于`8K`,用`url-loader`转成`base64` ,否则使用`file-loader`来处理文件
                                fallback: {
                                    loader: "file-loader",
                                    options: {
                                        esModule: false,
                                        name: "[name].[hash:8].[ext]",
                                        outputPath: "static", //打包之后文件存放的路径, dist/static
                                    }
                                },
                            }
                        }
                    ]
                },
                // ...
            ],
        },
        // ...
    }
    
    <!-- App.vue -->
    <template>
        <div>
            <img src="./static/vue.jpg" alt="" class="vue">
            <img src="./static/vue-large.png" alt="" class="vue-large">
            <div class="example">{{ msg }}</div>
        </div>
    </template>
    
    <script>
    export default {
        name: "App",
        data: () => ({
            msg: "Example"
        })
    }
    </script>
    
    <style scoped>
        .vue{
             100px;
        }
        .vue-large{
             300px;
        }
        .example{
            font-size: 30px;
        }
    </style>
    

    之后运行npm run dev,就可以看到效果了,可以在控制台的Element中看到,小于8K的图片被直接转成了base64,而大于8K的文件被当作了外部资源进行引用了。

    <!-- ... -->
    <img data-v-7ba5bd90="" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASA..." alt="" class="vue">
    <img data-v-7ba5bd90="" src="http://localhost:3000/static/vue-large.b022422b.png" alt="" class="vue-large"> 
    <!-- ... -->
    

    处理Babel

    使用babel主要是为了做浏览器的兼容,@babel/corebabel核心包,@babel/preset-env是集成bebal一些可选方案,可以通过修改特定的参数来使用不同预设,babel-loader可以使得ES6+ES5babel默认只转换语法而不转换新的APIcore-js可以让不支持ES6+ API的浏览器支持新API,当然也可以用babel-polyfill,相关区别可以查阅一下,建议用core-js,处理完成babelcommit id5e0f5ad

    $ yarn add -D @babel/core @babel/preset-env babel-loader
    
    $ yarn add core-js@3
    

    之后在根目录新建一个babel.config.js,然后将以下代码写入。

    // babel.config.js
    module.exports = {
        "presets": [
            [
                "@babel/preset-env",
                {
                    "useBuiltIns": "usage",
                    "corejs": 3,
                    "modules": false
                }
            ]
        ]
    }
    

    之后修改一下App.vue,写一个较新的语法?.

    <!-- App.vue -->
    <template>
        <div>
            <img src="./static/vue.jpg" alt="" class="vue">
            <img src="./static/vue-large.png" alt="" class="vue-large">
            <div class="example">{{ msg }}</div>
            <button @click="toast">按钮</button>
        </div>
    </template>
    
    <script>
    export default {
        name: "App",
        data: () => ({
            msg: "Example"
        }),
        methods: {
            toast: function(){
                window?.alert("ExampleMessage");
            }
        }
    }
    </script>
    
    <style scoped>
        .vue{
             100px;
        }
        .vue-large{
             300px;
        }
        .example{
            font-size: 30px;
        }
    </style>
    

    还需要修改一下webpack.config.js

    // webpack.config.js
    
    // ...
    module.exports = {
        // ...
        module: {
            rules: [
                // ...
                {
                    test: /.js$/,
                    exclude: /node_modules/,
                    use: ["babel-loader"]
                },
            ],
        },
        // ...
    }
    

    之后运行npm run dev就可以看到运行起来并且功能正常了,并且这个?.的语法在此处实际上是进行了一次转码,可以在控制台的Source里搜索ExampleMessage这个字符串就可以定位到相关位置了,然后就可以看到转码的结果了。

    window?.alert("ExampleMessage");
    // ->
    window === void 0 ? void 0 : window.alert("ExampleMessage");
    

    处理Css

    通常我们一般不只写原生css,我一般使用sass这个css框架,所以此处需要安装sass以及sass-loadersass-loader请使用低于@11.0.0的版本,sass-loader@11.0.0不支持vue@2.6.14,此外我们通常还需要处理CSS不同浏览器兼容性,所以此处需要安装postcss-loader,当然postcss.config.js也是可以通过postcss.config.js文件配置一些信息的,比如@/别名等,另外需要注意,在use中使用loader的解析顺序是由下而上的,例如下边的对于scss文件的解析,是先使用sass-loader再使用postcss-loader,依次类推,处理完成sasspostcsscommit idf679718

    yarn add -D sass sass-loader@10.1.1 postcss postcss-loader 
    

    之后简单写一个示例,新建文件/src/common/styles.scss,然后其中写一个变量$color-blue: #4C98F7;

    $color-blue: #4C98F7;
    

    之后修改App.vuewebpack.config.js,然后运行npm run dev就可以看到Example这个文字变成了蓝色。

    <!-- App.vue -->
    <template>
        <div>
            <img src="./static/vue.jpg" alt="" class="vue">
            <img src="./static/vue-large.png" alt="" class="vue-large">
            <div class="example">{{ msg }}</div>
            <button @click="toast">按钮</button>
        </div>
    </template>
    
    <script>
    export default {
        name: "App",
        data: () => ({
            msg: "Example"
        }),
        methods: {
            toast: function(){
                window?.alert("ExampleMessage");
            }
        }
    }
    </script>
    
    <style scoped lang="scss">
        @import "./common/styles.scss";
        .vue{
             100px;
        }
        .vue-large{
             300px;
        }
        .example{
            color: $color-blue;
            font-size: 30px;
        }
    </style>
    
    // webpack.config.js
    
    // ...
    module.exports = {
        // ...
        module: {
            rules: [
                // ...
                {
                    test: /.css$/,
                    use: [
                        "vue-style-loader",
                        "css-loader",
                        "postcss-loader"
                    ],
                },
                {
                    test: /.(scss)$/,
                    use: [
                        "vue-style-loader",
                        "css-loader",
                        "postcss-loader",
                        "sass-loader",
                    ]
                },
                // ...
            ],
        },
        // ...
    }
    
    

    加入VueRouter

    使用Vue大概率是需要Vue全家桶的VueRouter的,此处直接安装vue-router

    $ yarn add vue-router
    

    在这里改动比较多,主要是建立了src/router/index.js文件,之后建立了src/components/tab-a.vuesrc/components/tab-b.vue两个组件,以及承载这两个组件的src/views/framework.vue组件,之后将App.vue组件仅作为一个承载的容器,以及src/main.js引用了VueRouter,主要是一些VueRoute的一些相关的用法,改动较多,建议直接运行版本库,相关commit id96acb3a

    <!-- src/components/tab-a.vue -->
    <template>
        <div>Example A</div>
    </template>
    
    <script>
    export default {
        name: "TabA"
    }
    </script>
    
    <!-- src/components/tab-b.vue -->
    <template>
        <div>Example B</div>
    </template>
    
    <script>
    export default {
        name: "TabB"
    }
    </script>
    
    <!-- src/views/framework.vue -->
    <template>
        <div>
            <img src="../static/vue.jpg" alt="" class="vue">
            <img src="../static/vue-large.png" alt="" class="vue-large">
            <div class="example">{{ msg }}</div>
            <button @click="toast">按钮</button>
            <div>
                <router-link to="/tab-a">TabA</router-link>
                <router-link to="/tab-b">TabB</router-link>
                <router-view />
            </div>
    
        </div>
    </template>
    
    <script>
    export default {
        name: "FrameWork",
        data: () => ({
            msg: "Example"
        }),
        methods: {
            toast: function(){
                window?.alert("ExampleMessage");
            }
        }
    }
    </script>
    
    <style scoped lang="scss">
        @import "../common/styles.scss";
        .vue{
             100px;
        }
        .vue-large{
             300px;
        }
        .example{
            color: $color-blue;
            font-size: 30px;
        }
    </style>
    
    <!-- src/App.vue -->
    <template>
        <div>
            <router-view />
        </div>
    </template>
    
    // src/router/index.js
    import Vue from "vue";
    import VueRouter from "vue-router";
    
    Vue.use(VueRouter);
    
    import FrameWork from "../views/framework.vue";
    import TabA from "../components/tab-a.vue";
    import TabB from "../components/tab-b.vue";
    
    
    const routes = [{
        path: "/",
        component: FrameWork,
        children: [
            {
                path: "tab-a",
                name: "TabA",
                component: TabA,
            },{
                path: "tab-b",
                name: "TabB",
                component: TabB,
            } 
        ]
    }]
    
    export default new VueRouter({
        routes
    })
    
    // src/main.js
    import Vue from "vue";
    import App from "./App.vue";
    import Router from "./router/index";
    
    const app = new Vue({
        router: Router,
        ...App,
    });
    app.$mount("#app");
    

    加入Vuex

    同样使用Vue也是需要Vue全家桶的Vuex的,此处直接安装vuex

    yarn add vuex
    

    之后主要是新建了src/store/index.js作为store,修改了src/views/framework.vue实现了一个从store中取值并且修改值的示例,最后在src/main.js引用了store,相关commit ida549808

    // src/store/index.js
    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    const state = {
        text: "Value"
    }
    
    const getters = {
        getText(state) {
            return state.text;
        }
    }
    
    const mutations = {
        setText: (state, text) => {
            state.text = text;
        }
    }
    
    export default new Vuex.Store({
        state,
        mutations,
        getters
    });
    
    <!-- src/views/framework.vue -->
    <template>
        <div>
            <section>
                <img src="../static/vue.jpg" alt="" class="vue">
                <img src="../static/vue-large.png" alt="" class="vue-large">
                <div class="example">{{ msg }}</div>
                <button @click="toast">Alert</button>
            </section>
            <section>
                <router-link to="/tab-a">TabA</router-link>
                <router-link to="/tab-b">TabB</router-link>
                <router-view />
            </section>
            <section>
                <button @click="setVuexValue">Set Vuex Value</button>
                <div>{{ text }}</div>
            </section>
    
        </div>
    </template>
    
    <script>
    import { mapState } from "vuex";
    export default {
        name: "FrameWork",
        data: () => ({
            msg: "Example"
        }),
        computed: mapState({
            text: state => state.text
        }),
        methods: {
            toast: function(){
                window?.alert("ExampleMessage");
            },
            setVuexValue: function(){
                this.$store.commit("setText", "New Value");
            }
        }
    }
    </script>
    
    <style scoped lang="scss">
        @import "../common/styles.scss";
        .vue{
             100px;
        }
        .vue-large{
             300px;
        }
        .example{
            color: $color-blue;
            font-size: 30px;
        }
        section{
            margin: 10px;
        }
    </style>
    
    // src/main.js
    import Vue from "vue";
    import App from "./App.vue";
    import Store from "./store";
    import Router from "./router";
    
    const app = new Vue({
        router: Router,
        store: Store,
        ...App,
    });
    app.$mount("#app");
    

    配置ESLint

    正常情况下开发我们是需要配置ESLint以及prettier来规范代码的,所以我们需要配置一下,配置完成ESLintcommit id9ca1b7b

    $ yarn add -D eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue prettier vue-eslint-parser
    

    根目录下建立.editorconfig.eslintrc.js.prettierrc.js,进行一些配置,当然这都是可以自定义的,不过要注意prettiereslint规则冲突的问题。

    <!-- .editorconfig -->
    root = true
    
    [*]
    charset = utf-8
    indent_style = space
    indent_size = 4
    end_of_line = lf
    insert_final_newline = true
    trim_trailing_whitespace = true
    
    // .prettierrc.js
    module.exports = {
        "printWidth": 100, // 指定代码长度,超出换行
        "tabWidth": 4, // tab 键的宽度
        "useTabs": false, // 不使用tab
        "semi": true, // 结尾加上分号
        "singleQuote": false, // 使用单引号
        "quoteProps": "preserve", // 不要求对象字面量属性是否使用引号包裹
        "jsxSingleQuote": false, // jsx 语法中使用单引号
        "trailingComma": "es5", // 确保对象的最后一个属性后有逗号
        "bracketSpacing": true, // 大括号有空格 { name: 'rose' }
        "jsxBracketSameLine": false, // 在多行JSX元素的最后一行追加 >
        "arrowParens": "avoid", // 箭头函数,单个参数不强制添加括号
        "requirePragma": false, // 是否严格按照文件顶部的特殊注释格式化代码
        "insertPragma": false, // 是否在格式化的文件顶部插入Pragma标记,以表明该文件被prettier格式化过了
        "proseWrap": "preserve", // 按照文件原样折行
        "htmlWhitespaceSensitivity": "ignore", // html文件的空格敏感度,控制空格是否影响布局
        "endOfLine": "lf" // 结尾是 
     
     
    
     auto
    }
    
    // .eslintrc.js
    module.exports = {
        parser: "vue-eslint-parser",
        extends: [
            "eslint:recommended",
            "plugin:prettier/recommended",
            "plugin:vue/recommended",
            "plugin:prettier/recommended",
        ],
        parserOptions: {
            ecmaVersion: 2020,
            sourceType: "module",
        },
        env: {
            browser: true,
            node: true,
            commonjs: true,
            es2021: true,
        },
        rules: {
            // 分号
            "semi": "error",
            // 对象键值引号样式保持一致
            "quote-props": ["error", "consistent-as-needed"],
            // 箭头函数允许单参数不带括号
            "arrow-parens": ["error", "as-needed"],
            // no var
            "no-var": "error",
            // const
            "prefer-const": "error",
            // 允许console
            "no-console": "off",
        },
    };
    

    我们还可以配置一下lint-staged,在ESLint检查有错误自动修复,无法修复则无法执行git add

    $ yarn add -D lint-staged husky
    $ npx husky install
    $ npx husky add .husky/pre-commit "npx lint-staged"
    
    // package.json
    {
      // ...
      "lint-staged": {
        "*.{js,vue,ts}": [  "eslint --fix" ]
      }
    }
    

    配置TypeScript

    虽然是Vue2ts支持相对比较差,但是至少对于抽离出来的逻辑是可以写成ts的,可以在编译期就避免很多错误,对于一些Vue2 +TS的装饰器写法可以参考之前的博客 uniapp小程序迁移到TS
    ,本次的改动比较大,主要是配置了ESLint相关信息,处理TSVue文件的提示信息,webpack.config.js配置resolve的一些信息以及ts-loader的解析,对于.vueTS装饰器方式修改,src/sfc.d.ts作为.vue文件的声明文件,VueRouterVuexTS修改,以及最后的tsconfig.json用以配置TS信息,配置TypeScript完成之后的commit id0fa9324

    yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser @babel/plugin-syntax-typescript typescript vue-property-decorator vue-class-component ts-loader vuex-class
    
    // .eslintrc.js
    module.exports = {
        parser: "vue-eslint-parser",
        extends: ["eslint:recommended", "plugin:prettier/recommended"],
        overrides: [
            {
                files: ["*.ts"],
                parser: "@typescript-eslint/parser",
                plugins: ["@typescript-eslint"],
                extends: ["plugin:@typescript-eslint/recommended"],
            },
            {
                files: ["*.vue"],
                parser: "vue-eslint-parser",
                extends: [
                    "plugin:vue/recommended",
                    "plugin:prettier/recommended",
                    "plugin:@typescript-eslint/recommended",
                ],
            },
        ],
        parserOptions: {
            ecmaVersion: 2020,
            sourceType: "module",
            parser: "@typescript-eslint/parser",
        },
        // ...
    };
    
    // src/sfc.d.ts
    declare module "*.vue" {
        import Vue from "vue/types/vue";
        export default Vue;
    }
    
    <!-- src/views/framework.vue -->
    <!-- ... -->
    <script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    import { State } from "vuex-class";
    @Component
    export default class FrameWork extends Vue {
        protected msg = "Example";
        @State("text") text!: string;
        protected toast() {
            window?.alert("ExampleMessage");
        }
        protected setVuexValue() {
            this.$store.commit("setText", "New Value");
        }
    }
    </script>
    <!-- ... -->
    
    // tsconfig.json
    {
        "compilerOptions": {
          "target": "esnext",
          "module": "esnext",
          "strict": true,
          "jsx": "preserve",
          "importHelpers": true,
          "moduleResolution": "node",
          "esModuleInterop": true,
          "allowSyntheticDefaultImports": true,
          "experimentalDecorators":true,
          "sourceMap": true,
          "skipLibCheck": true,
          "baseUrl": ".",
          "types": [],
          "paths": {
            "@/*": [
              "./src/*"
            ]
          },
          "lib": [
            "esnext",
            "dom",
            "es5",
            "ES2015.Promise",
          ]
        },
        "exclude": [ "node_modules" ]
    }
    
    // webpack.config.js
    
    // ...
    module.exports = {
        mode: process.env.NODE_ENV,
        entry: "./src/index",
        output: {
            filename: "index.js",
            path: path.resolve(__dirname, "dist"),
        },
        resolve: {
            extensions: [".js", ".vue", ".json", ".ts"],
            alias: {
                "@": path.join(__dirname, "./src"),
            },
        },
        // ...
        module: {
            rules: [
                // ...
                {
                    test: /.(ts)$/,
                    loader: "ts-loader",
                    exclude: /node_modules/,
                    options: {
                        appendTsSuffixTo: [/.vue$/],
                    },
                },
                // ...
            ],
        },
        // ...
    };
    
    

    每日一题

    https://github.com/WindrunnerMax/EveryDay
    

    参考

    https://juejin.cn/post/6989491439243624461
    https://juejin.cn/post/6844903942736838670
    https://segmentfault.com/a/1190000012789253
    
  • 相关阅读:
    Ubuntu各种indicator汇总
    python 命令行参数获取
    python 内置方法的时间复杂度
    SkipList 跳表
    HBase Snapshot功能介绍
    HBase内部操作日志说明
    HBase参数配置及说明
    HBase 在HDFS 上的目录树
    第一章 重构,第一个案例
    shell来start、stop、restart应用程序模板
  • 原文地址:https://www.cnblogs.com/WindrunnerMax/p/15419190.html
Copyright © 2011-2022 走看看