webpack5搭建vue3,从0到1搭建项目

1,450 阅读4分钟

1. 新建项目

$ git clone https://gitee.com/Jennica824/okayDemo.git

$ cd okayDemo

// 初始化package.json
$ npm install y

安装webpackwebpack-cli

$ npm install webpack webpack-cli --save-dev
// webpack 核心功能
// webpack-cli 命令行工具

2. package.json

2.1. 私有安装包

{
+   "private": true, // 确保安装包是私有的,
-    // "main": "index.js", // 移除main入口,防止意外发布代码
}

2.2. dependencies

生产环境下,项目运行所需依赖,但是开发环境的依赖模块也可以配置到这里。

2.3. devDependencies

开发环境下,项目所需依赖。

2.4. 配置运行环境

// npm run start 开发运行
// npm run build 生产打包
"scripts": {
    "build": "webpack --config ./config/webpack.prod.js",
    "start": "webpack serve --config ./config/webpack.dev.js"
},

3. 配置

3.1 新建webpack文件夹

|-- webpack
|   |-- webpack.common.js                           // 公用配置
|   |-- webpack.dev.js                              // 开发时的配置
└   └── webpack.prod.js                             // 打包构建时的配置

3.2 webpack.common.js 中配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 根据模板文件生成html文件。

/**
 * 配置信息中webpack的五大模块
 * 1. mode:模式,通过选择production,development,none这三个值来告诉webpack使用相应的内置优化。
 * 2. entry:设置入口文件。
 * 3. output:打包出的文件存放处。
 * 4. module.rules:loader(加载器),webpack本身只支持处理js,json文件,通过相应的loader将其他格式的文件进行处理,转换成有效的模块。
 * 5. plugins:插件,loader用于处理不支持的类型的文件,而plugin则可以用于执行范围更广的任务。
 */
module.exports = (webpackEnv) => {
  const isEnvDevelopment = webpackEnv === 'development';
  const isEnvProduction = webpackEnv === 'production';

  return {
    mode: webpackEnv,
    entry: './src/index.js',
    output: {
      filename: 'main.js',
      path: path.resolve(__dirname, 'dist'),
      assetModuleFilename: 'asset/[name].[contenthash:8][ext][query]',
    },
    module: {
      rules: [{
        test: /\.css$/,
        use: [
          "style-loader",
          "css-loader",
          {
            // css兼容性处理
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  [
                    'postcss-preset-env',
                    {
                      autoprefixer: {
                        flexbox: 'no-2009',
                      },
                      stage: 3,
                    },
                  ],
                ],
              },
            }
          }
        ],
      }, {
        test: /\.(png|svg|jpg|jpeg|gif)$/,
        type: 'asset',
        generator: {
          filename: 'image/[name].[contenthash:8][ext][query]'
        }
      }]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: path.resolve(__dirname, '../public/index.ejs')
      }),
    ],
  };
};

* 3.3. webpack.common.jspulgins 的html模板文件:index.html 文件

在根目录下新建 public/index.html 文件,用于生成html模版

<!-- public/index.ejs -->

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

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app" />
    <title>Webpack5 demo</title>
</head>

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

</html>

注意::这里如果用 .html 后缀的模板,html-webpack-plugin 始终无法正常的生成html,然后改为了 .ejs 后就正常了。

3.4 webpack.dev.js 中配置

const webpackCommonConfig = require('./webpack.common.js')('development');

module.exports = {
  devServer: {
    host: 'localhost', // 指定host,,改为0.0.0.0可以被外部访问
    port: 8081, // 指定端口号
    open: true, // 服务启动后自动打开默认浏览器
    historyApiFallback: true, // 当找不到页面时,会返回index.html
    hot: true, // 启用模块热替换HMR,在修改模块时不会重新加载整个页面,只会更新改变的内容
    compress: true, // 启动GZip压缩
    https: false, // 是否启用https协议
    proxy: { 
       // 启用请求代理,可以解决前端跨域请求的问题
      '/api': '[请求地址]:[端口号]',
    },
  },
  ...webpackCommonConfig,
};

3.5 webpack.prod.js 中配置

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin")

module.exports = merge(common, {
    mode: 'production',
    optimization: {
        minimizer: [
            // 压缩js
            new TerserPlugin({}),
            // 压缩css
            new OptimizeCSSAssetsPlugin({}),
        ],
        // 抽离代码
        splitChunks: {
            chunks: "all",
        }
    },
    plugins: [
        // 压缩 大于30K
        new CompressionPlugin({
            test: /\.(js|css)$/,
            threshold: 30720,
        })
    ]
});

4. 运行项目

4.1. 启动

$ npm start
http://localhost:8081/

4.2. 打包

$ npm run build

image.png

$ npm install --save-dev optimize-css-assets-webpack-plugin --legacy-peer-deps

5. 集成 VUE3

5.1 安装插件

要让 webpack 识别 .vue 文件,必须安装 vue-loader 插件。

$ npm install vue --save

$ npm install vue-loader --save-dev

注意:Vue2.x 时安装的是 vue-template-complier

vue-loader:解析和转换 .vue 文件,提取出其中的逻辑代码 <script>、样式代码 <style>、以及 HTML 模版 <template>,再分别把它们交给对应的 Loader 去处理。

5.2 在 webpack.common.js 中配置 webpack 识别 vue 文件

// webpack.common.js

    module: {
      rules: [{
        test: /\.vue$/,
        use: ['vue-loader']
      }]
    },

5.3. npm run devnpm run build 报错,解决办法

image.png 删除 node-modules 文件夹和 package-lock.json 文件,然后重新 npm install image.png 注意 解决办法:在根目录下新建 webpack.config.js

const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader/dist/index');

module.exports = {
    mode: 'development', // 环境模式

    entry: path.resolve(__dirname, './src/main.js'), // 打包入口

    output: {
        path: path.resolve(__dirname, 'dist'), // 打包出口
        filename: 'js/[name].js' // 打包完的静态资源文件名
    },

    plugins: [
        // 每次打包,先把dist文件夹清空(必备)
        new CleanWebpackPlugin,
        // 添加 VueLoaderPlugin 插件
        new VueLoaderPlugin,
        // 解析为 html 模版
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, './public/index.html'), // html模版地址
            filename: 'index.html', // 打包后输出的文件夹名
            title: 'webpack5搭建vue3项目'
        })
    ],

    module: {
        rules: [
            { test: /\.vue$/, use: ['vue-loader'] },
            { test: /\.css$/, use: ['style-loader', 'css-loader'] }
        ]
    }
}
{
    ...
    
    "scripts": {
        "build": "NODE_ENV=production webpack --progress --config webpack.config.js",
        "dev": "webpack serve --config webpack.config.js"
      },
      
      ...
}

再次运行,问题解决,但是不能用 config 文件夹内的配置,具体什么原因,还没找到

5.4. router 配置 vue-router

1. 安装依赖
$ npm install vue-router@4 -S
2. 创建 home.vuetest.vue
<template>
  <div>
      首页
  </div>
</template>
3. 创建 router.js 文件
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../src/views/home.vue';
import Test from '../src/views/test.vue'

const routerHistory = createWebHistory();

const router = createRouter({
    history: routerHistory,
    routes: [
        {
            path: '/',
            name: 'Home',
            component: Home
        },
        {
            path: '/test',
            name: 'Test',
            component: Test
        }
    ]
})

export default router;
4. 修改 main.js 文件
import { createApp } from 'vue'
import App from './App.vue'

+ import router from '../router/routre'

createApp(App)
+ .use(router)
.mount('#root')
5. 修改 app.vue
<template>
    <div>
        <router-view />
    </div>
</template>

5.5. store 配置 VUEX / Pinia

1. 安装依赖 vuex4
$ npm install vuex@next --save

2. 创建 vuex 对应的 store/index.js

// src/store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

3. 安装依赖 Pinia

$ npm install pinia

4. Pinia 对应的 store/index.js

// main.ts
import { createPinia } from 'pinia'
app.use(createPinia())

// store/index.js
import { defineStore } from 'pinia'
export const todos = defineStore('todos', {
  state: () => ({
    /** @type {{ text: string, id: number, isFinished: boolean }[]} */
    todos: [],
    /** @type {'all' | 'finished' | 'unfinished'} */
    filter: 'all',
    // type 会自动推断为 number
    nextId: 0,
  }),
  getters: {
    finishedTodos(state) {
      // 自动完成! ✨
      return state.todos.filter((todo) => todo.isFinished)
    },
    unfinishedTodos(state) {
      return state.todos.filter((todo) => !todo.isFinished)
    },
    /**
     * @returns {{ text: string, id: number, isFinished: boolean }[]}
     */
    filteredTodos(state) {
      if (this.filter === 'finished') {
        // 自动调用其他 getter ✨
        return this.finishedTodos
      } else if (this.filter === 'unfinished') {
        return this.unfinishedTodos
      }
      return this.todos
    },
  },
  actions: {
    // 任何数量的参数,返回一个 Promise 或者不返回
    addTodo(text) {
      // 你可以直接改变状态
      this.todos.push({ text, id: this.nextId++, isFinished: false })
    },
  },
})

[注意]:Piniavuex 不能同时存在。 Pinia 的具体使用,请转接下文 🔗

6. 配置核心功能

6.1. ES6+ES5

由于有些浏览器无法解析 ES6+ 等高级语法,需要将其转化为浏览器能解析的低级语法.

1. 安装依赖
$ npm install @babel/core babel-loader @babel/preset-env -D

* 踩坑

1. css解析处理

image.png

// 安装loader
$ npm install --save-dev style-loader css-loader --legacy-peer-deps
// package.json
devDependencies:{
     "css-loader": "^6.7.1",
     "style-loader": "^3.3.1",
}
// webpack/webpack.common.js
 module: {
      rules: [{
        test: /\.css$/,
        use: [ "style-loader", "css-loader" ]
      }]
 }

2. vue-loader 版本过高导致的问题

image.png