foreword
Hello everyone, I'm Lin Sanxin, uses the most simple and easy-to-understand words to describe the most difficult knowledge points. is my motto. is the premise of .
background
When everyone is developing Vue, most of them use the ready-made Vue scaffolding Vue-cli for development, but after using it for so long, don't you want to build your own Vue-cli
?
Today I will take you to build a basic Vue-cli
, which can also give you a deeper understanding of Webpack
! I recommend everyone to follow me step by step!
Note in advance: This article only introduces the basic configuration of vue-cli. Regarding optimization and specification, I will write two more articles to explain it later.
1. Create a folder
Create a new folder my-vue-cli to store the project
2. Initialize npm
type in the terminal
npm init
Then just press Enter, so that the project can have an npm management environment, and then we can install the packages we need on this environment.
3、webpack、webpack-cli
install webpack、webpack-cli
webpack
: packaged toolswebpack-cli
: Tool to provide command line for webpacknpm i webpack webpack-cli -D
4、src、public
Create two new folders, src、public
, in the root directory. The former is used to place the main code of the project, and the latter is used to place the public static resources of the project.
public/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.0"> <title>my-vue-cli</title> </head> <body> <div id="app"></div> </body> </html>
src/main.js
import { add } from './tools/add.js' console.log(add(1, 2)) console.log('我是main.js')
src/tools/add.js
export const add = (a, b) => { return a + b }
5. Entry file
The main.js
just now is our entry file, which is equivalent to the root node of the entire reference tree. Webpack packaging needs to start searching from the entry file until all reference files are packaged.
Configure the entry file and create a new webpack.config.js
in the root directory:
const path = require('path')
module.exports = {
// 模式 开发模式
mode: 'development',
// 入口文件 main.js
entry: {
main: './src/main.js'
},
// 输出
output: {
// 输出到 dist文件夹
path: path.resolve(__dirname, './dist'),
// js文件下
filename: 'js/chunk-[contenthash].js',
// 每次打包前自动清除旧的dist
clean: true,
}
}
6. Configure the packaging command
Configure the packaging command in package.json
:
"scripts": {
"build": "webpack"
},
Now we enter npm run build
into the terminal, and we can find that the packaging is successful:
But this is actually not our purpose. Our purpose is to insert the final packaged js file into the index.html
just now, because the js file must be referenced by the html file, which makes sense! So we not only have to package js, but also package html
Little knowledge: loader and plugin
loader
: Enable webpack to parse non-js files, such as css, png, ts, etc.plugin
: Expand the packaging function of webpack, such as optimizing the volume, displaying the progress bar, etc.
7. Package html
Packaging html needs to use the plugin html-webpack-plugin
, which is plugin, so you need to install it:
npm i html-webpack-plugin -D
And it needs to be configured in webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 刚刚的代码...
// 插件都放 plugins 中
plugins: [
new HtmlWebpackPlugin({
// 选择模板 public/index.html
template: './public/index.html',
// 打包后的名字
filename: 'index.html',
// js文件插入 body里
inject: 'body',
}),
]
}
Now we can execute the package command npm run build
in the terminal and we can see that the html is packaged, and the packaged html is automatically imported into the packaged js file
Now we can open the packaged index.html
and find that the console can output, indicating success!
Packaging CSS
Create a new folder src
under styles
to store style files
src/styles/index.scss
body { background-color: blue; }
Then we introduce it in the entry file
main.js
import './styles/index.scss' // 刚刚的代码...
Our purpose is to package the file
index.scss
and letindex.html
automatically import the packaged css file, so we need to install the following things:sass、sass-loader
: Can convert scss code to csscss-loader
: Enable webpack to pack csssass-resources-loader
: optional, supports packaging global public scss filesmini-css-extract-plugin
: css code can be packaged into a single css file
Let's install these plugins
npm i
sass
sass-loader
sass-resources-loader
mini-css-extract-plugin
-D
Then configure webpack.config.js
// 刚才的代码...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 刚才的代码...
plugins: [
// 刚才的代码...
new MiniCssExtractPlugin({
// 将css代码输出到dist/styles文件夹下
filename: 'styles/chunk-[contenthash].css',
ignoreOrder: true,
}),
],
module: {
rules: [
{
// 匹配文件后缀的规则
test: /\.(css|s[cs]ss)$/,
use: [
// loader执行顺序是从右到左
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
// {
// loader: 'sass-resources-loader',
// options: {
// resources: [
// // 放置全局引入的公共scss文件
// ],
// },
// },
],
},
]
}
}
At this point, we re-execute the packaging command npm run build
, and we can find that the packaged css file appears, and the css file is automatically introduced in index.html
:
We can look at the page, and we can see that the background of the body has turned blue, indicating that it has an effect:
package pictures
0622ff33111aeb has been abandoned in url-loader
, and asset-module
can be used to package images. We first place an image in src/assets/images
:
And rewrite index.css
body {
width: 100vw;
height: 100vh;
// 引入背景图片
background-image: url('../assets/images/guang.png');
background-size: 100% 100%;
}
Then we add the configuration of the packaged image in webpack.config.js
module: {
rules: [
// 刚刚的代码...
{
// 匹配文件后缀的规则
test: /\.(png|jpe?g|gif|svg|webp)$/,
type: 'asset',
parser: {
// 转base64的条件
dataUrlCondition: {
maxSize: 25 * 1024, // 25kb
}
},
generator: {
// 打包到 dist/image 文件下
filename: 'images/[contenthash][ext][query]',
},
}
]
}
We now re-run npm run build
and find that the folder images
already exists under dist
Let's take a look at the page background image has taken effect, indicating that the packaging is successful!
configure babel
babel
can convert the high-level grammar in our project into a relatively low-level grammar, for example, ES6
can be converted to ES5
, which is compatible with some low-level browsers, so it is necessary
First install the required packages:
@babel/core、babel-loader
: tool for converting syntax@babel/preset-env
: A set of ready-made rules for conversion@babel/plugin-transform-runtime
: plugin required to convert async/awaitnpm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime -D
Since
babel
is a syntax conversion for js files, we need to operate on js inwebpack.config.js
module: { rules: [ // 刚刚的代码... { // 匹配js后缀文件 test: /\.js$/, // 排除node_modules中的js exclude: /node_modules/, use: [ 'babel-loader' ], } ] }
It is not enough to configure
babel-loader
alone, we also need to configure the rules forbabel
conversion, so we need to createbabel.config.js
in the root directory// babel.config.js module.exports = { presets: [ // 配置规则 "@babel/preset-env" ], // 配置插件 plugins: ["@babel/plugin-transform-runtime"] }
At this point, we re-run the package
npm run build
, and we can find that in the packaged js code, theES6
syntax in the code just now has been converted toES5
syntax! You can see thatconst
in the code just now has been converted toES5
syntax
Packaging Vue
Packaging Vue requires the following packages:
vue
: Dependencies required for Vue developmentvue-loader
: loader for parsing.vue
filesvue-template-compiler
: A tool for parsing templates in vue@vue/babel-preset-jsx
: support parsing jsx syntax in vue
Note:vue
andvue-template-compiler
versions need to be consistent, here I use the2.6.14
version,vue-loader
here I use the15.9.8
version
So let's install it first:
npm i
vue@2.6.14 vue-template-compiler@2.6.14
vue-loader@15.9.8 @vue/babel-preset-jsx
-D
Then we need to go to webpack.config.js
to configure the parsing of the .vue
file
// 刚才的代码...
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
// 刚才的代码...
plugins: [
// 刚才的代码...
new VueLoaderPlugin()
],
module: {
rules: [
// 刚才的代码...
{
test: /\.vue$/,
use: 'vue-loader',
}
]
}
}
And configure it in babel.config.js
to let webpack support the jsx
syntax in the .vue
file
module.exports = {
presets: [
"@babel/preset-env",
// 支持vue中的jsx语法
"@vue/babel-preset-jsx"
],
plugins: ["@babel/plugin-transform-runtime"]
}
Now we can create a new one under src
App.vue
<template>
<div class="box">我是App哈哈哈哈</div>
</template>
<script>
export default {}
</script>
<style lang="scss">
.box {
width: 500px;
height: 200px;
color: #fff;
background-color: #000;
}
</style>
Then rewrite src/main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: (h) => h(App),
}).$mount('#app')
At this point, we re-run npm run build
, we can see the effect of the page, indicating that the packaging is successful!
Configure path aliases
Sometimes there are too many layers of file references, and the references will look ambiguous, such as ../../../../../App.vue
, so we can configure the alias alia
module.exports = {
// 刚才的代码...
resolve: {
// 路径别名
alias: {
'@': path.resolve('./src'),
assets: '~/assets',
tools: '~/tools'
},
// 引入文件时省略后缀
extensions: ['.js', '.ts', '.less', '.vue'],
},
}
Now the alias configuration is complete:
- Before configuration:
../../../../../App.vue
- After configuration:
@/App.vue
webpack-dev-server
We just found out that every time the code is changed, it has to be repackaged, which is very cumbersome. Is there any way to change the code and repackage it automatically? This will use webpack-dev-server
npm i webpack-dev-server -D
Configure webpack.config.js
in devServer
devServer: {
// 自定义端口号
// port:7000,
// 自动打开浏览器
open: true
},
Then go to package.json
and configure the startup command
"scripts": {
"build": "webpack",
"serve": "webpack serve"
},
At this point, we can start the project by running npm run serve
!
Differentiate the environment
We can't configure all the configurations in one webpack.config.js
, because we have two environments development (development environment), production (production environment), so we create the
build folder in the root directory and create three files
webpack.base.js
: shared configuration for both environments- Ingress, output configuration
- Handling of various documents
- progress bar display
- path alias
webpack.dev.js
: Unique configuration for development environment- webpack-dev-server
- Different source-map modes
- different environment variables
webpack.prod.js
: Unique configuration for production environment- Different source-map modes
- different environment variables
We need to install a merge plugin webpack-merge
first, the configuration for the two environments can merge the common configuration
npm i webpack-merge -D
Then we create a new build folder in the root directory, and create a new
webpack.base.js、webpack.dev.js、webpack.config.js
under this folder
webpack.base.js
// 公共配置 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const { VueLoaderPlugin } = require('vue-loader') module.exports = { // 入口文件 main.js entry: { main: './src/main.js' }, // 输出 output: { // 输出到 dist文件夹 // 记得改路径 path: path.resolve(__dirname, '../dist'), // js文件下 filename: 'js/chunk-[contenthash].js', // 每次打包前自动清除旧的dist clean: true, }, plugins: [ new HtmlWebpackPlugin({ // 选择模板 public/index.html template: './public/index.html', // 打包后的名字 filename: 'index.html', // js文件插入 body里 inject: 'body', }), new MiniCssExtractPlugin({ // 将css代码输出到dist/styles文件夹下 filename: 'styles/chunk-[contenthash].css', ignoreOrder: true, }), new VueLoaderPlugin() ], module: { rules: [ { // 匹配文件后缀的规则 test: /\.(css|s[cs]ss)$/, use: [ // loader执行顺序是从右到左 MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', // { // loader: 'sass-resources-loader', // options: { // resources: [ // // 放置全局引入的公共scss文件 // ], // }, // }, ], }, { // 匹配文件后缀的规则 test: /\.(png|jpe?g|gif|svg|webp)$/, type: 'asset', parser: { // 转base64的条件 dataUrlCondition: { maxSize: 25 * 1024, // 25kb } }, generator: { // 打包到 dist/image 文件下 filename: 'images/[contenthash][ext][query]', }, }, { test: /\.js$/, // 排除node_modules中的js exclude: /node_modules/, use: [ 'babel-loader' ], }, { test: /\.vue$/, use: 'vue-loader', } ] }, resolve: { // 路径别名 alias: { '@': path.resolve('./src'), assets: '~/assets' }, // 引入文件时省略后缀 extensions: ['.js', '.ts', '.less', '.vue'] }, }
webpack.dev.js
// 开发环境 const { merge } = require('webpack-merge') const base = require('./webpack.base') module.exports = merge(base, { mode: 'development', devServer: { open: true, // hot: true, } })
webpack.prod.js
// 生产环境 const { merge } = require('webpack-merge') const base = require('./webpack.base') module.exports = merge(base, { mode: 'production' })
Then we go to package.json
to modify the instructions
"scripts": {
"serve": "webpack serve --config ./build/webpack.dev",
"build": "webpack --config ./build/webpack.prod"
},
Next we ran these two commands and found that both were successful:
npm run build
npm run serve
build progress bar
Whether it is starting the project or packaging, the progress bar needs to be displayed, so the progress bar needs to be configured in webpack.base
, we need to install the progress bar plugin progress-bar-webpack-plugin
npm i progress-bar-webpack-plugin -D
// webpack.base.js
// 刚刚的代码...
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const chalk = require('chalk')
module.exports = {
// 刚刚的代码...
plugins: [
// 刚刚的代码...
new ProgressBarPlugin({
format: ` build [:bar] ${chalk.green.bold(':percent')} (:elapsed seconds)`,
})
],
// 刚刚的代码...
}
Now we can see that there will be a progress bar whether starting the project or packaging
source-map
The function of source-map
: When the code reports an error, it can quickly locate the error location. All webpack5
source-map modes of can be viewed on the webpack official website: https://webpack.docschina.org/configuration/devtool/#root
Here I use two modes:
development
: Useeval-cheap-module-source-map
mode, can locate the source code location and source code display, suitable for development mode, small sizeproduction
: Usingnosources-source-map
, only the source code location can be located, but the source code cannot be displayed, the size is small, suitable for production mode
So we start configuring source-map
webpack.dev.js
// 刚才的代码... module.exports = merge(base, { // 刚才的代码... devtool: 'eval-cheap-module-source-map' })
webpack.prod.js
// 刚才的代码... module.exports = merge(base, { // 刚才的代码... devtool: 'nosources-source-map' })
environment variable
Configure the environment variables of these two environments devlopment、production
webpack.dev.js
// 刚才的代码... const webpack = require('webpack') module.exports = merge(base, { // 刚才的代码... plugins: [ // 定义全局变量 new webpack.DefinePlugin({ process: { env: { NODE_DEV: JSON.stringify('development'), // 这里可以定义你的环境变量 // VUE_APP_URL: JSON.stringify('https://xxx.com') }, }, }), ] })
webpack.prod.js
// 刚才的代码... const webpack = require('webpack') module.exports = merge(base, { // 刚才的代码... plugins: [ // 定义全局变量 new webpack.DefinePlugin({ process: { env: { NODE_DEV: JSON.stringify('prodction'), // 这里可以定义你的环境变量 // VUE_APP_URL: JSON.stringify('https://xxx.com') }, }, }), ] })
Optimize and standardize
Regarding optimization and specification, I will write two more articles.
Epilogue
I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends and fish together haha, touch the fish group, add me, please note [Si No]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。