初探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{
        width: 100px;
    }
    .vue-large{
        width: 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{
        width: 100px;
    }
    .vue-large{
        width: 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{
        width: 100px;
    }
    .vue-large{
        width: 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{
        width: 100px;
    }
    .vue-large{
        width: 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{
        width: 100px;
    }
    .vue-large{
        width: 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" // 結尾是 \n \r \n\r 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM