前言
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…