关于前端:从零开始webpack5vue30vuextypescriptantdesignvue最新实践报告

15次阅读

共计 7691 个字符,预计需要花费 20 分钟才能阅读完成。

前言

vue3.0 曾经出了一段时间了,网上也有大量的实际教程可供参考。但从理论登程,因为开发的 chrome 插件须要降级到 vue3.0,同时筹备换上 ant-design-vue,因为没有应用 vue-cli 脚手架,在这个降级过程中还是踩了不少坑,于是将本人搭建整个我的项目的过程记录下来,如对大家有帮忙,防止少踩坑也是极好的。(没有 vue-router 是因为 chrome 插件用不上)

该文章记录日期为2020 年 11 月 23 日,但我的项目理论搭建日期应该是在一周前左右。因为目前 vue3.0 刚出不久,包含其配套生态也还处于 beta 版本或者刚出 RC 版,后续可能呈现一些变动,该实际仅作为目前版本的参考。

顺带一提,若想用上 vue3.0 下比拟成熟的 UI 框架,目前除了 ant-design-vue 和 vant 并没有太多抉择,如 Vuetify,Quasar 等优良的 UI 框架都还没适配 vue3.0,能够急躁等一等。

一、webpack5 根底环境配置

1. 在我的项目下新建一个 package.json 配置文件

npm init -y

2. 装置 webpack5,webpack-cli 和 webpack-dev-server

阐明一下,就在 4 天前和 19 天前,webpack 和 webpack-cli 又更新了版本,webpack 为 5.6.0, webpack-cli 为 4.2.0。而我搭建时的版本为 webpack 5.4.0

npm i webpack webpack-cli webpack-dev-server -D

这里呈现第一个坑,webpack-cli 若为 4.2.0 版本,应用 webpack-dev-server 会报错 Error: Cannot find module 'webpack-cli/bin/config-yargs',因为版本之间不兼容造成。
解决形式,临时将 webpack-cli 版本降为 3.3.12

3. 装置所需依赖

Less 和 CSS
因为咱们应用了 ant-design, CSS 预编译就用 less。

npm i less less-loader css-loader style-loader -D

TypeScript

npm i typescript ts-loader -D

Babel

npm i @babel/core @babel/preset-env @babel/plugin-transform-runtime babel-loader babel-plugin-import -D
npm i @babel/runtime -S

Vue 和 Vuex(指定版本)

npm i vue@next vuex@4.0.0-rc.1

这里遇见第二个坑,之前的 vue-template-compiler 模板编译在这个版本会报错,改用@vue/compiler-sfc

npm i vue-loader@16.0.0-rc.1 @vue/compiler-sfc -D

Ant-design-vue(需指定版本)

npm i ant-design-vue@2.0.0-beta.15 -S

相干 webpack 插件

npm i clean-webpack-plugin copy-webpack-plugin html-webpack-plugin mini-css-extract-plugin -D

二、新建我的项目文件

文件目录

--index.html
--src
    --main.ts
    --shims-vue.d.ts
    --pages
        --App.vue
    --store
        --Actions.ts
        --Getters.ts
        --Mutations.ts
        --State.ts
        --index.ts
    --assets
        --imgs
            --xxx.png
--tsconfig.json
--webpack.dev.config.js
--webpack.prod.config.js
--package.json

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

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

</html>

main.ts

import {createApp} from "vue";
import box from "./pages/App.vue";
import store from './store';
import Button from 'ant-design-vue/lib/Menu';
import 'ant-design-vue/dist/antd.less';

const app = createApp(box);
app.use(store).use(Button).mount('#app');

store/index.ts

import {createStore} from 'vuex';
import state from './State';
import mutations from './Mutations';
import actions from './Actions';
import getters from './Getters';

const store = createStore({
    state,
    mutations,
    actions,
    getters
})

export default store;

store/Mutations.ts

interface mutationObj {
    addCount: Function,
    [propName: string]: any
}

const mutations: mutationObj = {addCount(state) {state.counter += 1;}
}

export default mutations;

store/State.ts

interface stateObj {
    counter: number,
    [propName: string]: any
}

const state: stateObj = {counter: 0}

export default state;

App.vue

<template>
    <div class="box">
        {{counter}}
        <a-button type="primary" @click="addCount"> 增加 </a-button>
    </div>
</template>

<script lang="ts">
import {computed} from "vue";
import {useStore} from "vuex";
export default {
    name: "App",
    // 组合 API 函数入口
    setup() {const store: any = useStore();

        const counter = computed(() => {return store.state.counter;});

        const addCount = () => {store.commit("addCount");
        };

        return {counter, addCount};
    },
};
</script>

<style lang="less" scoped>
.box {
    width: 300px;
    height: 300px;
    background: red;
}
</style>

三、配置文件

webpack.dev.config.js

const path = require('path');
const {HotModuleReplacementPlugin, IgnorePlugin} = require('webpack');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {VueLoaderPlugin} = require('vue-loader');

const config = {
    mode: 'development',
    entry: './src/main.ts',
    output: {
        filename: 'index.js',
        path: path.resolve(__dirname, 'dist')
    },
    devServer: {
        contentBase: false,
        // publicPath: './dist',
        hot: true,
        port: 8080,
        open: true,
        // hotOnly: true,
        compress: true,
        overlay: true
    },
    watchOptions: {ignored: /node_modules/},
    plugins: [new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Hot Module Replacement',
            template: 'index.html'
        }),
        new IgnorePlugin(/^\.\/locale$/, /moment$/),
        new HotModuleReplacementPlugin(),
        new VueLoaderPlugin()],
    module: {
        rules: [
            // babel 应用 runtime,防止将不须要的代码注入
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        // cacheDirectory: true,
                        presets: ['@babel/preset-env'],
                        plugins: [
                            '@babel/plugin-transform-runtime',
                            ['import', {
                                "libraryName": "antd",
                                "style": true,   // or 'css'
                            }, 'antd']
                        ]
                    }
                }],
            },
            {
                test: /\.ts$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'ts-loader',
                        options: {
                            // 指定特定的 ts 编译配置,为了辨别脚本的 ts 配置
                            configFile: path.resolve(__dirname, './tsconfig.json'),
                            appendTsSuffixTo: [/\.vue$/]
                        }
                    }
                ]
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.less$/,
                use: ['style-loader', 'css-loader',
                    {
                        loader: 'less-loader',
                        options: {
                            lessOptions: {
                                modifyVars: {
                                    'primary-color': '#4608e2',
                                    'link-color': '#4608e2',
                                    'border-radius-base': '20px',
                                },
                                javascriptEnabled: true,
                            }
                        }
                    }],
            }
        ]
    },
    resolve: {extensions: ['.js', '.ts']
    },
};


module.exports = (env) => {console.log(` 以后执行 ${env.mode}模式 `);
    return config;
}

webpack.prod.config.js

const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {VueLoaderPlugin} = require('vue-loader');

const config = {
    mode: 'production',
    entry: './src/main.ts',
    output: {
        filename: 'index.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Hot Module Replacement',
            template: 'index.html'
        }),
        new CopyWebpackPlugin({
            patterns: [
                {
                    from: 'src/assets',
                    to: 'assets'
                },
            ]
        }),
        new MiniCssExtractPlugin({filename: './index.css'}),
        new VueLoaderPlugin()],
    module: {
        rules: [
            // babel 应用 runtime,防止将不须要的代码注入
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                        presets: ['@babel/preset-env'],
                        plugins: [
                            ['import', {
                                "libraryName": "antd",
                                "style": true,   // or 'css'
                            }, 'antd']
                        ]
                    }
                }],
            },
            {
                test: /\.ts$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'ts-loader',
                        options: {
                            // 指定特定的 ts 编译配置,为了辨别脚本的 ts 配置
                            configFile: path.resolve(__dirname, './tsconfig.json'),
                            appendTsSuffixTo: [/\.vue$/]
                        }
                    }
                ]
            },
            {
                test: /\.vue$/,
                use: ['vue-loader']
            },
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', {
                    loader: 'less-loader',
                    options: {
                        lessOptions: {
                            modifyVars: {
                                'primary-color': '#4608e2',
                                'link-color': '#4608e2',
                                'border-radius-base': '20px',
                            },
                            javascriptEnabled: true,
                        }
                    }
                }]
            }
        ]
    },
    resolve: {extensions: ['.js', '.ts']
    },
};

module.exports = (env) => {console.log(` 以后执行 ${env.mode}模式 `);
    return config;
}

tsconfig.json

{
    "compilerOptions": {
        // "esModuleInterop": true,
        "incremental": true, // 增量编译
        "allowJs": true,
        "lib": [
            "es6",
            "dom",
            "es2017"
        ],
        "types": []},
    "exclude": ["node_modules"]
}

shims-vue.d.ts

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

declare module "vue/types/vue" {
    interface Vue {
        $http: any;
        $Message: any;
        $Modal: any;
    }
}

package.json

{
  "name": "test",
  "version": "1.0.0",
  "description": "","main":"index.js","scripts": {"test":"echo \"Error: no test specified\" && exit 1","start":"webpack-dev-server --config webpack.dev.config.js --env.mode development --watch --profile","build":"webpack --config webpack.prod.config.js --env.mode production"},"keywords": [],"author":"",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/plugin-transform-runtime": "^7.12.1",
    "@babel/preset-env": "^7.12.7",
    "@vue/compiler-sfc": "^3.0.2",
    "babel-loader": "^8.2.1",
    "babel-plugin-import": "^1.13.3",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^6.3.2",
    "css-loader": "^5.0.1",
    "html-webpack-plugin": "^4.5.0",
    "less": "^3.12.2",
    "less-loader": "^7.1.0",
    "mini-css-extract-plugin": "^1.3.1",
    "style-loader": "^2.0.0",
    "ts-loader": "^8.0.11",
    "typescript": "^4.1.2",
    "vue-loader": "^16.0.0-rc.1",
    "webpack": "^5.6.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  },
  "dependencies": {
    "@babel/runtime": "^7.12.5",
    "ant-design-vue": "^2.0.0-beta.15",
    "vue": "^3.0.2",
    "vuex": "^4.0.0-rc.1"
  }
}

四、命令执行

开发环境

npm start

生产环境

npm run build

五、最终成果

六、结语

这个我的项目搭建的案例,只做了最简略的示例,就是利用 vuex 做一个计数器。其实 chrome 插件会有多个入口文件,最终打包进去的文件构造和开发时还是有所区别,前面会独自写篇文章介绍。

我将该示例放在了 github 上,欢送食用~
https://github.com/gxy5202/we…

正文完
 0