手动搭建vue2.0项目-webpack5

1,446 阅读5分钟

前言:最近公司正在做前端微服务拆分,各个服务分开打包;为了跟上脚步,动手用webpack搭建了一套vue项目;里面涉及了许多插件介绍不清楚的请自行百度,这样会加深印象

本文针对有前端基础,对webpack了解,vue项目开发
为了加深自己的理解和记忆整理下来分享给大家,跟着走一遍大概能理解webpack是在做什么事情
希望对大家有所帮助

思考

  • vue/react脚手架做了哪些事情
  • webpack在脚手架里面充当了什么角色
  • 你对webpack的理解是什么

1、环境搭建

软件安装

node.js安装+Visual Studio Code安装

node和npm版本 image.png

package.json中依赖的插件版本 image.png

2、初始化项目

npm init

帮助我们快速初始化 package.json 文件中文文档

mkdir project-vue
cd project-vue
npm init          // 这里可以直接写 npm init -y,不需要手动回车,接受默认值

image.png

查看 package.json  image.png

Tips:npm安装插件简写命令 image.png

“dependencies”     生产环境中中需要的
“devDependencies”  开发和测试中需要的

webpack+webpack-cli

webpack 是一个现代 JavaScript 应用程序的静态模块打包器中文文档

npm i webpack webpack-cli -D

安装的版本号,与官网一致webpack5.72.0 image.pngimage.png

打包几要素:

  • 1、入口起点
  • 2、出口配置
  • 3、配置模式

新增build/webpack.config.js

const path = require('path')               // 引入node内置模块path

module.exports ={
  mode: "development",                     // 开发模式
  entry:'./src/main.js',                   // 入口文件,把src下的main.js编译到出口文件
  output:{                                 // 出口文件
    path:path.resolve(__dirname,'dist'),   // 出口路径和目录
    filename: "[name].js",                 // 编译后的名称
  },
}

新增src/main.js

console.log('project-vue');

新增命令行(package.json)

{
  "scripts": {
    、、、
    "build": "webpack --config=build/webpack.config.js"
  },
}

执行命令npm run build image.png

build文件夹生成了dist/main.js

image.png

需要把所有打包出的文件自动添加到我们可以访问的index.html,继续看下面

3、生成index.html

html-webpack-plugin

生成一个 HTML 文件,把打包生成的依赖加载进来

npm i html-webpack-plugin -D

新增index.html文件(项目根据该文件作为模版打包)

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>webpack搭建vue项目</title>
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>

修改配置build/webpack.config.js

、、、
const HtmlWebpackPlugin = require('html-webpack-plugin') // 构建html文件

module.exports ={
  、、、
  plugins:[
    new HtmlWebpackPlugin({                // 自动插入到dist目录中
      title: 'webpack搭建vue项目',
    }),
  ],
}

执行命令,生成了main.js和index.html image.png

把根目录下的index.html作为模版文件传入到html-webpack-plugin插件里面

、、、

module.exports ={
  、、、
  plugins:[
    new HtmlWebpackPlugin({                // 自动插入到dist目录中
      、、、
      template:'./index.html',
    }),
  ],
}

执行命令

npm run build

对比dist/index.html和根目录index.html,发现多了一行代码,其他的全部copy过来了。 好处是可以自己预先定义好自己所需的资源 image.png image.png

4、核心Vue

vue项目当然需要解析vue文件,并且还能支持vue的<template>模块写法,顺便支持jsx的写法(可不添加)

vue+vue-loader+vue-template-compiler

解析.vue文件

npm i vue@2 vue-loader vue-template-compiler -D

新增src/App.vue

<template>
  <div id="app">
    <h1>
      hello,world!
    </h1>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

修改src/main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  el:'#app',
  render: h => h(App)
})

修改配置build/webpack.config.js

、、、
const VueLoaderPlugin = require('vue-loader/lib/plugin'); // vue-loader编译vue文件

module.exports ={
  、、、
  resolve:{  // 此配置用在引用文件时
    extensions: ['.js', '.vue', '.json'],  // 引入路径是不用写对应的后缀名
    alias:{
      'vue$':'vue/dist/vue.esm.js',        // 正在使用的是vue的运行时版本,而此版本中的编译器是不可用的,我们需要把它切换成运行时 + 编译的版本
      '@': path.resolve(__dirname,'../src'), // 用@直接指引到src目录下
    }
  },
  plugins:[
    、、、
    new VueLoaderPlugin(),
  ],
  module: {
    rules: [
      { 
        test: /\.vue$/,
        loader: 'vue-loader'
      },
    ]
  }
}

前方有大坑:请注意。。。。 这个坑查了很多的资料,配置了好久才搞好,反正说的都不清不楚的。

执行命令,报错找不到vue-loader/lib/plugin的模块;百度了一下,也搜了其他的配置发现不行,高版本vue-loader+@vue/compiler-sfc试了不行。不知道是不是兼容的vue3项目,大家有遇到过么? image.png

找到node_modules下的该插件发现确实没有这个模块

image.png

解决办法:版本降级@15

npm un vue-loader 
npm i vue-loader@15 -D
npm run build

打开dist/index.html文件,展示hello,world

vue-router

vue路由配置,vue3.x对应vue-router4.x,vue2.x需要指定版本官方文档

image.png

npm i vue-router@3 -D

src下新增common/loginPage.vue、common/mainPage.vue

loginPage.vue :
<template>
  <div>
    <div>
      <h1>用户登录</h1>
      <p>
        <label>用户名:</label>
        <input>
      </p>
      <p>
        <label>密码:</label>
        <input>
      </p>
      <button @click="login">登录</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "loginPage",
  methods: {
    login() {
      this.$router.push({
        name: "Main"
      })
    },
  }
}
</script>

mainPage.vue :
<template>
  <div>
    <div>
      <button>excel导入导出功能</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "mainPage",
}
</script>

在src下新增router/index.js

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

let routes = [];
// 登录
const loginRoute = {
  path: "/login",
  name: "Login",
  component: () => import('../common/loginPage') 
};
// 首页
const mainRoute = {
  path: "/main",
  name: "Main",
  component: () => import('../common/mainPage') ,
};

routes.push(loginRoute, mainRoute);

routes.unshift({
  path: "*",
  redirect: routes[0].path,
});

const router = new Router({
  routes,
});

export default router;

修改src/main.js

、、、
import router from "./router"

new Vue({
  、、、
  router,
  、、、
})

修改src/App.vue

<template>
  <div id="app">
    <!-- 注入router -->
    <router-view />
  </div>
</template>
、、、

执行命令

npm run build

页面效果

1.gif

查看项目目录

image.png

像vuex、还有一些三方UI库的引入都和在脚手架配置的差不多,这里就不一一介绍了。可以等搭建完成以后再添加这些功能

5、支持Css

你可以试试写一下样式,打包就报错,不能写css可不行;这里我用的是less预处理器,与sass相比就是轻一点

vue-style-loader+css-loader+less-loader+postcss-loader+autoprefixer

解析样式

npm i vue-style-loader css-loader less-loader postcss-loader autoprefixer -D

Tips:

  • vue-style-loader是基于 style-loader fork 过来的,跟 style-loader 类似
  • vue-style-loader除了插入 style 外,还做了一些服务端渲染的支持
  • vue-style-loade和style-loader只需要安装一种,都支持样式热更新
  • PostCSS是一款使用插件转换CSS的工具,可以用autoprefixer自动补全浏览器前缀

新增样式,修改loginPage.vue

<template>
  <div class="login">
    <div class="login-form">
       、、、
    </div>
  </div>
</template>
、、、

<style lang="less" scoped>
.login {
  height: 100%;
  width: 100%;
  overflow: hidden;
  display: flex;

  .bg-img {
    height: 100%;
    width: 100%;
  }

  .login-form {
    position: absolute;
    top: 25%;
    left: 35%;
    width: 400px;
    height: 250px;
    border: 1px solid #ffffff;
    opacity: 1;
    background: #ffffff;
    border-radius: 10px;
  }
}
</style>

修改配置build/webpack.config.js

、、、

module.exports ={
  、、、
  module: {
    rules: [
      、、、
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader',
        ],
      },
      {
        test: /.less$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
    ]
  }
}

新增postcss.config.js

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

修改package.json

{
  、、、
  "browserslist": [
    "> 1%",
    "last 2 versions",
  ]
}

执行命令

npm run build

页面效果 image.png

查看浏览器控制台

image.png

6、支持文件、字体、图片等

asset模块

webpack5 内置了asset模块, 可以代替 file-loader、url-loader、raw-loader 等插件处理静态资源,咋们先不要管怎么配置的,能用就行,以后我们再深入了解

修改配置build/webpack.config.js

、、、

module.exports ={
  、、、
  module: {
    rules: [
      、、、
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024,
          },
        },
        generator: {
          filename: 'images/[base]',
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        type: 'asset',
        generator: {
          filename: 'files/[base]',
        },
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        type: 'asset',
        generator: {
          filename: 'media/[base]',
        },
      },
    ]
  }
}

新增src/assets文件夹,放一张背景图bg.png

添加图片,修改loginPage.vue

<template>
  <div class="login">
    <img :src="bgUrl" class="bg-img"/>
    、、、
  </div>
</template>

<script>
import bg from "../assets/imgs/bg.jpeg";
export default {
  name: "loginPage",
  data() {
    return {
      bgUrl: bg,
    }
  },
}
</script>

执行命令

npm run dev

image.png

7、热更新

现在文件里只有一个命令就是打包的,如果有了项目需要开发、调试,不希望写完页面就更新?

webpack-dev-server

webpack-dev-server 主要提供两个功能:

  • 为静态文件提供服务
  • 自动刷新和热替换(HMR)
npm i webpack-dev-server -D

修改配置build/webpack.config.js

、、、

module.exports ={
  、、、
  devServer: {
    open: true,                            // 自动打开浏览器
    hot: true,                             // 热更新
    port: '8888'                           // 指定端口8888
  },
}

新增命令行package.json

"dev": "webpack serve --config build/webpack.config.js",

执行命令

npm run dev

8、环境变量

如何区分当前打包的是开发环境还是正式环境,或者需要根据环境来获取不同的变量;和vue脚手架下配置.env一样

cross-env

运行跨平台设置和使用环境变量的脚本

npm i cross-env -D

修改命令行package.json

"dev": "cross-env NODE_ENV=dev webpack serve --config build/webpack.config.js",
"build": "cross-env NODE_ENV=prod webpack --config=build/webpack.config.js"

修改配置build/webpack.config.js

、、、

console.log(process.env.NODE_ENV);

module.exports ={
  mode: process.env.NODE_ENV === 'prod' ? "production" : "development",    // 开发模式
  、、、
}

开发环境mode用development,因为追求的是打包速度,而不是体积

正式环境mode用production,因为追求的是体积小,而不是打包速度

执行命令

npm run dev / npm run build

对比一下控制台输出的文件大小和打包速度

dotenv-webpack

支持程序获取.env配置的环境变量

npm i dotenv-webpack -D

修改配置build/webpack.config.js

、、、

const Dotenv = require('dotenv-webpack'); // 支持程序获取.env配置的环境变量

module.exports ={
  、、、
  optimization: {
    nodeEnv: false
  },
  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, `../.env.${process.env.NODE_ENV}`)
    })
  ]
  、、、
}

新增.env.dev和.env.prod

.env.dev

NODE_ENV = dev
API_URL = https://mayGongDev.com

.env.prod
NODE_ENV = prod
API_URL = https://mayGongProd.com

修改App.vue

、、、
<script>
export default {
  、、、
  created() {
    console.log(process.env.NODE_ENV);
    console.log(process.env.API_URL);
  }
}
</script>
、、、

执行命令

npm run dev / npm run build

查看浏览器控制台

image.pngimage.png

9、Babel

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中

babel-loader+@babel/core+@babel/preset-env+@babel/runtime-corejs3+@babel/plugin-transform-runtime+core-js+@vue/babel-preset-jsx

解析es6语法和api官方文档

npm i babel-loader '@babel/core' '@babel/preset-env' '@babel/plugin-transform-runtime' @vue/babel-preset-jsx -D

npm i @babel/runtime-corejs3 core-js -S

Tips:

  • babel-loader 是将ES6等高级语法转换为能让浏览器能够解析的低级语法
  • @babel/core 是babel的核心模块,编译器,提供转换的API
  • @babel/preset-env 可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为es5
  • @babel/plugin-transform-runtime 它会帮我自动动态require @babel/runtime 中的内容
  • @babel/runtime-corejs3 就是提供统一的模块化的helper, 使用能大大减少打包编译后的体积
  • core-js JavaScript标准库的polyfill,@babel/preset-env 引用的包
  • @vue/babel-preset-jsx 支持解析vue中的jsx语法

修改loginPage.vue

、、、

<script>
export default {
  methods: {
    login() {
      this.getUser().then(res => {
        if (res === '1') {
          this.$router.push({
            name: "Main"
          })
        }
      });
    },
    getUser() {
      return new Promise((resole) => {
        [1,2,3].map((res) => {
          console.log(res);
        })
        if (true) {
          resole('1');
        }
      })
    }
  }
}
</script>
、、、

修改mainPage.vue

<script>
export default {
  name: "mainPage",
  render() {
    return <div>
      <div>
        <button>excel导入导出功能</button>
      </div>
    </div>;
  }
}
</script>

执行命令

npm run dev

查看浏览器控制台,发现箭头函数、Promise等没有解析,如果运行在不兼容的浏览器或其他地方就会报错 image.png

修改配置build/webpack.config.js

、、、

module.exports ={
  、、、
  module: {
    rules: [
      、、、
      {
        test: /\.(ts|js)x?$/,
        use: [
          'babel-loader',
        ],
        exclude: /node_modules/,
      },
    ]
  }
}

新增babel.config.js文件

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", // 按需引入
        "corejs": 3             // corejs版本
      }
    ],
    "@vue/babel-preset-jsx"     // 支持vue中的jsx语法
  ],
  plugins: [
    [
      "@babel/plugin-transform-runtime", 
      { 
        "corejs": 3             // corejs版本
      }
    ]
  ]
}

执行命令

npm run dev

查看浏览器控制台

image.png

10、优化项

可配置项

clean-webpack-plugin

清理文件

npm i clean-webpack-plugin -D

修改build/webpack.config.js

、、、
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 清理构建目录下的文件

module.exports ={
  、、、
  plugins:[
    、、、
    new CleanWebpackPlugin(),
  ],
  、、、
}

或者不用插件,直接配置

、、、
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 清理构建目录下的文件

module.exports ={
  、、、
  output: { // 出口文件
    、、、
    clean: true
  },
  、、、
}

progress-bar-webpack-plugin

控制台打包时显示进度条

npm i progress-bar-webpack-plugin -D

修改配置build/webpack.config.js

、、、
const ProgressBarWebpackPlugin = require('progress-bar-webpack-plugin') // 设置打包进度条

module.exports ={
  stats: "errors-only",                    // 日志打印只打印错误和警告
  、、、
  plugins:[
    、、、
    new ProgressBarWebpackPlugin({
      complete: "█",
      clear: true
    }),
  ],
}

打包效果

1.gif

friendly-errors-webpack-plugin

控制台打包时优化提示

npm i friendly-errors-webpack-plugin -D

修改配置build/webpack.config.js

、、、
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin') // 优化提示

module.exports ={
  、、、
  plugins:[
    、、、
    new FriendlyErrorsWebpackPlugin({
      compilationSuccessInfo: {           // 成功的时候输出
        messages: [
          'You can now view project-vue in the browser.'
        ],
      },
      clearConsole: true,                  // 是否每次都清空控制台
    }),
  ],
}

mini-css-extract-plugin+css-minimizer-webpack-plugin

css样式分离和css样式压缩

npm i mini-css-extract-plugin css-minimizer-webpack-plugin -D

修改配置build/webpack.config.js

、、、
const MiniCssExtractPlugin = require('mini-css-extract-plugin');       // webpack4以后 改为此插件 css样式分离
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");    // webpack5 改为此插件 css样式压缩

module.exports = {
  、、、
  plugins:[
    、、、
    new MiniCssExtractPlugin({
      filename: process.env.NODE_ENV === 'prod' ? 'css/[name].[contenthash].css' : 'css/[name].css',
      chunkFilename: process.env.NODE_ENV === 'prod' ? 'css/[name].[contenthash].css' : 'css/[name].css',
    }),
    new CssMinimizerPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === 'prod' ? MiniCssExtractPlugin.loader : 'vue-style-loader',
          'css-loader',
        ],
      },
      {
        test: /.less$/,
        use: [
          process.env.NODE_ENV === 'prod' ? MiniCssExtractPlugin.loader : 'vue-style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader',
        ],
      },
    ],
  },
}

打包效果

image.png

11、git提交支持Eslint校验

husky+lint-staged

git commit 代码检查

npm i lint-staged husky -D // 安装插件

npx husky install   // 创建.husky, 会在根目录下创建.husky文件夹

npx husky add .husky/pre-commit // 添加pre-commit hooks,会生成一个pre-commit执行脚本

大坑又来了:不能手动新建.husky文件以及pre-commit,手动新建会无法触发commit的hooks

把代码推到gitee或其他代码托管网站上,再执行npx husky install image.png

修改.husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo 'eslint校验代码'

执行命令

git commit -m '代码校验'

查看输出 image.png

eslint+prettier

规范代码,统一风格,自动修复

npm i eslint prettier -D

./node_modules/.bin/eslint --init

选择自己项目所需的,根目录下生成.eslintrc.js文件 image.png image.png image.png image.png image.png image.png

最终生成的文件夹

image.png

打开build/webpack.config.js发现报错‘require‘ is not defined no-undef image.png

修改.eslintrc.js

module.exports = {
  env: {
    、、、
    node: true        // node环境
  },
  、、、
}

打开mainPage.vue发现报错 image.png

修改.eslintrc.js

module.exports = {
  、、、
  parserOptions: {
    ecmaFeatures:{
      "jsx": true            // 支持jsx语法
    },
    ecmaVersion: "latest",
    sourceType: "module"
  },
  、、、
}

git校验

git commit时 检查暂存的代码是否符合代码规范,报错并输出在控制台

修改package.json

{
  "scripts": {
    、、、
    "lint": "npx lint-staged"
  }
  、、、
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "npx eslint --fix",
      "git add"
    ]
  }
}

修改.husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo 'eslint校验代码'

npm run lint

修改.eslintrc.js

module.exports = {
  、、、
  rules: {
    "no-console": "error"  // 关闭console
  }
}

vscode安装ESLint扩展插件 image.png

执行命令git commit -m 'xxx',暂存区没有文件,因此也不校验 image.png

不规范代码已经标红,修改loginPage.vue image.png

执行命令 image.png

12、项目地址

project-vue

image.png

随着项目文件越来越多,越来越大,需要规范和配置的也会越来越多。

吐槽和建议,欢迎下方留言