头图

"Basic construction" starts from scratch and builds a Vue-Cli based on Webpack5

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 tools
  • webpack-cli : Tool to provide command line for webpack

    npm 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 let index.html automatically import the packaged css file, so we need to install the following things:

  • sass、sass-loader : Can convert scss code to css
  • css-loader : Enable webpack to pack css
  • sass-resources-loader : optional, supports packaging global public scss files
  • mini-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/await

    npm 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 in webpack.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 for babel conversion, so we need to create babel.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, the ES6 syntax in the code just now has been converted to ES5 syntax! You can see that const in the code just now has been converted to ES5 syntax

Packaging Vue

Packaging Vue requires the following packages:

  • vue : Dependencies required for Vue development
  • vue-loader : loader for parsing .vue files
  • vue-template-compiler : A tool for parsing templates in vue
  • @vue/babel-preset-jsx : support parsing jsx syntax in vue
Note: vue and vue-template-compiler versions need to be consistent, here I use the 2.6.14 version, vue-loader here I use the 15.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 : Use eval-cheap-module-source-map mode, can locate the source code location and source code display, suitable for development mode, small size
  • production : Using nosources-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]

image.png

2k 声望
7.1k 粉丝
0 条评论
推荐阅读
无代码工具!既省事,又能给你的简历增添色彩~
前言大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心。背景就在前几天,我的朋友分享给我一件他们公司技术团队的趣事:公司需要在两天内上线一款电商的小程序,这...

Sunshine_Lin2阅读 912

封面图
从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木148阅读 12.1k评论 10

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 5.9k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木65阅读 6.1k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木42阅读 7.3k评论 6

CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(&gt;^ω^&lt...

XboxYan42阅读 2.9k评论 14

封面图
2k 声望
7.1k 粉丝
宣传栏