webpack是对jacascript打包的工具,不能识别其他的文件,所以需要配置loader进行识别
├─build
│ ├─webpack.base.conf.js //开发环境和生产环境都有的操作配置文件
│ ├─webpack.dev.conf.js //开发环境配置文件
│ ├─webpack.prod.conf.js //生产环境配置文件
│ ├─webpack.rules.conf.js //模块匹配规则
├─node_modules //依赖文件
├─public //静态文件存储位置
| |-index.html //项目模板文件
└─src
| ├─plugins
| ├─router
| ├─store
| ├─views
| |-App.vue
| |-main.ts
| |-shims-vue.d.ts
|-.env.dev //环境文件
|-.env.test
|-.env.prod
|-.gitigore
|-babel.config.js //bable配置文件
|-package.json
|-postcss.config.js //适配规则
|-tsconfig.json
yarn init -y 初始化项目 //-y是都回答yes 一键初始化
// 安装webpack三件套
yarn add webpack webpack-cli webpack-dev-server -D
// -D 等价于--save-dev开发环境时的依赖
// -S 等价于--save生产环境时的依赖
//使用http://localhost:8080/webpack-dev-server/可以访问webpack-dev-server命令在 内存中生成的文件
├─build
│ ├─webpack.base.conf.js //公共配置
│ ├─webpack.dev.conf.js //mode为development配置
│ ├─webpack.prod.conf.js //mode为production配置
│ ├─webpack.rules.conf.js //loader配置
//webpack.base.conf.js
const path = require("path")
module.exports = function (proMode) {
return {
// JavaScript 执行入口文件
entry: path.resolve(__dirname, "../src/main.ts"),
output: {
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, "../dist"),
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: "./js/[name].[chunkhash].js",
// publicPath: "./",//生产环境时使用
clean: true
},
}
}
.env.dev
.env.test
.env.prod
//.env.dev
NODE_ENV="development"
VUE_APP_BASE_API="/api"
VUE_APP_API_USER="/user"
VUE_APP_SHOWCONSOLE=true
//Dotenv是一个零依赖模块,它将环境变量中的变量从.env文件加载到process.env中
// 安装dotenv
yarn add dotenv -D
yarn add cross-env -D
//webpack.base.conf.js
const webpack = require("webpack")
const envMode = process.env.envMode
require("dotenv").config({
path: `.env.${envMode}`
})
const prefixRE = /^VUE_APP_/
let env = {}
for (const key in process.env) {
if (key == "NODE_ENV" || key == "BASE_URL" || prefixRE.test(key)) {
env[key] = JSON.stringify(process.env[key])
}
}
module.exports = function (proMode) {
return {
plugins: [
new webpack.DefinePlugin({ // 定义环境和变量
'process.env': {
...env
}
}),
],
}
}
"scripts": {
"dev": "cross-env envMode=dev webpack serve --config ./build/webpack.dev.conf.js --color",
"build": "cross-env envMode=prod webpack --config build/webpack.prod.conf.js --color",
"build:test": "cross-env envMode=test webpack --config build/webpack.prod.conf.js --color"
},
//安装babel
yarn add @babel/core @babel/preset-env babel-loader -D
yarn add core-js -S
//webpack.rules.conf.js
module.exports = function (proMode) {
return [{
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
cacheDirectory: true
}
}
},
]
}
//安装ts
yarn add typescript -D
// 解析 typescript 的 babel 预
yarn add @babel/preset-typescript -D
//polyfill 箭头函数
yarn add @babel/plugin-transform-runtime -D
//支持 ts 类的写法
yarn add @babel/plugin-proposal-class-properties -D
//支持三点展开符
yarn add @babel/plugin-proposal-object-rest-spread -D
yarn add @babel/runtime @babel/runtime-corejs3 core-js -S
const presets = [
[
"@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": "3.19.1"
}
],
[
'@babel/preset-typescript', // 引用Typescript插件
{
allExtensions: true, // 支持所有文件扩展名,否则在vue文件中使用ts会报错
},
],
]
const plugins = [
//按需引入vant组件
[
"import",
{
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}, "vant"
],
[
'@babel/plugin-transform-runtime',
{
corejs: 3,
},
],
'@babel/proposal-class-properties',
'@babel/proposal-object-rest-spread',
]
module.exports = {
plugins,
presets
}
yarn add html-webpack-plugin -D
//webpack.base.conf.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const cdn = {
dev: {
css: [],
js: []
},
prod: {
css: [],
js: [
'https://lib.baomitu.com/vue/3.0.11/vue.runtime.global.prod.js',
"https://lib.baomitu.com/vue-router/4.0.6/vue-router.global.prod.min.js",
"https://lib.baomitu.com/vuex/4.0.0/vuex.global.prod.min.js"
]
}
}
module.exports = function (proMode) {
return {
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"), //引用模板位置
filename: "index.html", //生成的html,路径相对于output.path
title: "webpack+vue3",
minify: {
collapseWhitespace: true, //是否折叠空白
preserveLineBreaks: false,
minifyCSS: true, //是否压缩
minifyJS: true,
removeComments: false
},
files: proMode ? cdn.prod : cdn.dev
}),
],
}
}
// public/index.html
//需将模板中的 标签里的内容改成
<%= htmlWebpackPlugin.options.title %>
//CDN引入js
<% for (var i in
htmlWebpackPlugin.options.files&&htmlWebpackPlugin.options.files.js) { %>
<script src="<%= htmlWebpackPlugin.options.files.js[i] %>"></script>
<% } %>
//css-loader提取.css的内容
//style-loader把.css的内容按照javascript中的字符串存储起来,在网页执行javascript时通过DOM操作动态的往HTML HEAD标签里插入style标签
yarn add style-loader css-loader -D
yarn add less less-loader -D
yarn add autoprefixer postcss-loader -D
//webpack.rules.conf.js
module.exports = function (proMode) {
return [
{
test: /\.less/,
use: [MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
ident: "postcss",
plugin: () => {
require("postcss-preset-env")()
}
}
}
}, "less-loader"
]
},
{
test: /\.css$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
ident: "postcss",
plugin: () => {
require("postcss-preset-env")()
}
}
}
},
// "less-loader"
]
}
]
}
loader从后往前执行
//webpack.base.conf.js
resolve: {
alias: {
"@": path.resolve(__dirname, "../src")
},
//扩展名书写
//把项目中所有的扩展名都写上去,要不然会报错
extensions: [".js", ".vue", ".ts"]
},
//配置loader的解析规则
resolveLoader:{
//loader的寻找目录
modules:[
'node_modules',
]
}
//webpack.base.conf.js
output: {
clean: true
},
yarn add vue-loader@next @vue/compiler-sfc -D
yarn add vue@next -S
// webpack.rules.conf.js
rules: [
{
test: /\.vue$/,
use: [
'vue-loader'
]
}
]
//webpack.base.conf.js
const { VueLoaderPlugin } = require('vue-loader/dist/index');
plugins: [
new VueLoaderPlugin()
]
<template>
<div id="app">
<div id="nav" v-if="!$route.meta.hide">
<router-link to="/">Home</router-link>|
<router-link to="/about">About</router-link>
</div>
<keep-alive>
<router-view
v-if="$route.meta.keepAlive"
class="router"
></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" class="router"></router-view>
</div>
</template>
<script>
import { defineComponent, computed } from "vue";
import { useStore } from "vuex";
export default defineComponent({
setup() {
const store = useStore();
return {
title: computed(() => store.state.title),
};
},
});
</script>
<style lang="less">
// @import "style/_variables.scss";
#app {
// font-family: "Avenir", Helvetica, Arial, sans-serif;
// -webkit-font-smoothing: antialiased;
// -moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 20px 0;
a {
font-weight: bold;
color: #2c3e50;
font-size: 32px;
&.router-link-exact-active {
color: red;
}
}
}
h3 {
font-size: 18px;
}
a {
}
</style>
import { createApp } from 'vue' // Vue 3.x 引入 vue 的形式
import App from './App.vue' // 引入 APP 页面组建
const app = createApp(App) // 通过 createApp 初始化 app
app.mount('#app') // 将页面挂载到 root 节点
src/shims-vue.d.ts
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
//tsconfig.js
//includes把声明文件的路径加上,就不会了报错了
"types": ["./src/shims-vue.d.ts"],
yarn add vue-router@4 vuex@next axios -S
yarn add vant@next -S
//按需引入
yarn add babel-plugin-import -D
// babel.config.js
const plugins = [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
//在搭建脚手架的同时,要区分css文件和less文件
//vant组件不能使用less-loader去识别,会报错
//webpack.conf.rules.js
module.exports = function (proMode) {
return [
{
test: /\.(png|jpg|svg|gif)$/,
type: "asset/resource",
generator: {
filename: "assets/[hash:8].[name][ext]"
}
},
]
}
//使用copy-webpack-plugin插件时 ,要复制的文件夹里不能为空,否则报错
yarn add copy-webpack-plugin -D
//webpack.prod.conf.js
const copyWebpackPlugin = require("copy-webpack-plugin");
const webpackConfigProd = {
mode: "production",
plugins: [
new copyWebpackPlugin({
patterns: [{
from: path.resolve(__dirname, "../public"),
to: "./",
globOptions: {
dot: true,
gitignore: true,
ignore: ["**/index.html"],
}
}]
}),
],
}
yarn add mini-css-extract-plugin -D
//webpack.base.conf.js
const miniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = function (proMode) {
return {
plugins: [
new miniCssExtractPlugin({
filename: "./css/[name].[contenthash].css",
}),
],
}
}
//webpack.rules.conf.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = function (proMode) {
return [
{
test: /\.less/,
use: [MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
ident: "postcss",
plugin: () => {
require("postcss-preset-env")()
}
}
}
}, "less-loader"
]
},
{
test: /\.css$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
ident: "postcss",
plugin: () => {
require("postcss-preset-env")()
}
}
}
},
// "less-loader"
]
}
]
}
yarn add postcss-loader postcss-px-to-viewport -D
//新建postcss.config.js文件
const path = require("path")
module.exports = ({
file
}) => {
const designWidth = file.includes(path.join("node_modules", "vant")) ? 375 : 750;
return {
plugins: {
autoprefixer: {
overrideBrowserslist: [
"Android 4.1",
"iOS 7.1",
"Chrome > 31",
"ff > 31",
"ie >= 8"
]
},
"postcss-px-to-viewport": {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: designWidth, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: ['.ignore', '.hairlines', '.ig-'], // 需要忽略的CSS选择器
minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 568 // 横屏时使用的视口宽度
}
}
}
}
//webpack.prod.conf.js
const TerserPlugin = require('terser-webpack-plugin');
const webpackConfigProd = {
mode: "production",
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
extractComments: false,
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}),
]
}
}
//webpack.base.conf.js
module.exports = function (proMode) {
return {
cache: {
type: "filesystem",
buildDependencies: {
config: [__filename]
}
},
}
}
初始化容器
注册事件
触发容器
插件/plugin是构造函数或者是类
compilation在调用complier时会相应调用
初始化compilation钩子
添加资源
初始化配置
获取入口地址
读取入口文件内容
通过@babel/parser将其内容转换为抽象树
通过@babel/traverse收集依赖
如果当前文件有依赖
获取文件路径,生成绝对路径
同时添加到依赖数组中
编译代码,将代码中浏览器不能识别的语法进行编译
定义bundle字符串获取code内容
获取文件绝对路径
把这个代码写入文件里