前言
不知道什么时候,搭建一个前端项目,慢慢成为了我的执念,总感觉不搭建一个,总是缺点什么(虽然日常根本用不到,因为公司都是用那些集成脚手架,从
vue-cli
再到vite
),正是抱着这种心态,我写了这篇文章(从零开始搭建(基于webpack5+
),最后部署在github
)。
目标
满足日常的开发环境(
dev
)以及本地预览(preview
),和发布部署(github pages
)。
为了这个目标,我们需要做什么呢?我是拆分成两个目标
需处理的文件
scss/css
文件。js/json/ts/tsx
文件。vue
单文件。- 图片和字体以及其他静态资源。
需要支持的功能
- 本地开发文件热更新。
- 多环境/设置环境变量。
- 开发环境
- 生产环境
- 本地预览环境
- 分包构建时,不同的资源放入不同的文件夹。
- 第三方包打包时,分割成独立的文件。
开始冲!!!
代码实现
1. 创建文件夹
cmd
mkdir project
2. 生成package.json
cmd
npm init -y
3. 安装webpack
、webpack-cli
、webpack-dev-server
cmd
npm install --save-dev webpack webpack-cli webpack-dev-server
4. 现在项目已经搭建好了我们现在来分析下需处理的文件。
scss/css
文件。js/json/ts/tsx
文件。vue
单文件。- 图片和字体以及其他静态资源。
对于这些文件的处理,其实webpack
都已经提供了相应的loader
来处理。
sass-loader
、css-loader
、style-loader
可以来处理sass/css
文件。js
和json
,webpack
天然就支持,我们仅仅需要bebal-loader
来转化我们的js(es6+
),对于ts/tsx
我们可以使用@babel/preset-typescript
。- 至于
vue
单文件,我们可以使用vue
官方提供的vue-loader
来处理。 - 最后在说静态资源,我们可以使用
webpack
的资源模块(asset module)
来处理。
下面来配置我们webpack.common.js
webpack.common.js
const { VueLoaderPlugin } = require("vue-loader")
const { loader } = MiniCssExtractPlugin
module.exports = (env) => {
return {
entry: "./src/index.ts",
plugins: [
new VueLoaderPlugin()
],
resolve: {
extensions: [".vue", ".tsx", ".ts", ".js"],
},
module: {
rules: [
{
test: /\.vue$/i,
use: ["vue-loader"],
},
{
test: /\.s[ac]ss$/i,
use: [
env.mode === "development" ? "style-loader" : loader,
"css-loader",
"sass-loader",
],
},
{
test: /\.css$/i,
use: [
env.mode === "development" ? "style-loader" : loader,
"css-loader",
"sass-loader",
],
},
{
test: /\.(ts|tsx)?$/,
exclude: /(node_modules)/,
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
[
"@babel/preset-typescript",
{
allExtensions: true,
},
],
],
},
},
// 图片
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource"
},
// 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: "asset/resource"
},
],
},
}
}
针对ts
还需要在项目根目录创建tsconfig.json
tsconfig.json
{
"compilerOptions": {
"outDir": "./docs/",
"noImplicitAny": true,
"module": "ESNext",
"target": "ES2020",
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*",
],
"exclude": [
"node_modules"
]
}
自此我们的第一个目标已经完成,继续冲!!!
需要支持的功能
- 本地开发文件热更新。
- 多环境/设置环境变量。
- 开发环境
- 生产环境
- 本地预览环境
- 分包构建时,不同的资源放入不同的文件夹。
- 第三方包打包时,分割成独立的文件。
1. 本地开发文件热更新
这个我们可以使用webpack
提供的webpack-dev-server
来完成。 我们需要先配置webpack.dev.js
webpack-dev.js
const { merge } = require("webpack-merge")
const webpack = require("webpack")
const common = require("./webpack.common.js")
module.exports = merge(common({ mode: "development" }), {
mode: "development",
devServer: {
static: "./docs"
}
})
然后配置package.json
的script
命令
cmd
"start": "webpack serve --open --config webpack.dev.js",
我先来解释下上面的那个配置项,意思是,它会在把打包的文件,放到docs文件夹下(先说下这个为啥不用默认的dist文件夹,因为github page部署静态资源,它需要这个资源放在docs文件夹才可以,后面部署在详说 ),这个文件夹是不可见的(存在内存中),并且默认部署在localhost:8080
2. 多环境/设置环境变量
其实所谓的多环境,其实都是差不多的,都是把资源打包到一个文件夹,无非是生产环境的代码更小。 而对于环境变量的配置,我们可以使用DefinePlugin
来设置,这是官网对这个插件的描述:
下面我们来设置环境变量
开发环境
webpack.dev.js
const { merge } = require("webpack-merge")
const webpack = require("webpack")
const common = require("./webpack.common.js")
module.exports = merge(common({ mode: "development" }), {
mode: "development",
devtool: "inline-source-map",
devServer: {
static: "./docs",
// 忽略编译错误
client: {
overlay: false,
},
},
plugins: [
// 定义环境变量
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("development"),
serverUrl: JSON.stringify("dev"),
base: JSON.stringify("/base-dev"),
},
}),
],
})
生产环境
webpack.prod.js
const webpack = require("webpack")
const { merge } = require("webpack-merge")
const common = require("./webpack.common.js")
module.exports = merge(common({ mode: "production" }), {
mode: "production",
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production"),
serverUrl: JSON.stringify("pro"),
base: JSON.stringify("/base-pro"),
},
}),
],
})
本地预览环境
这里我想的是本地预览环境,其实就是我们本地提供一个node
服务,然后提供对docs
这些静态资源的访问,所以思路就是:我们在提供node
服务前,只要先行执行构建命令npm run build
就行。这里我们使用express
来搭建服务。
cmd
npm install --save-dev express open
open
是一个打开链接/应用的插件,这里我们利用它打开chrome
浏览器
server.js
const express = require('express')
const app = express()
const port = 3000
app.use(express.static('docs'))
app.listen(port, () => {
import('open').then(res => {
res.default(` 本机的Ip:${port}`, { app: { name: 'chrome' } })
})
})
然后修改package.json
文件
package.json
"scripts": {
"preview": "npm run build && node server.js"
}
这样执行命令,我们就可以浏览器看到我们的项目了。
3. 分包构建时,不同的资源放入不同的文件夹
首先我们来分析下,我们最终打包生成的资源,无非就几种,js、css、静态资源(图片和字体),所以我们只需要针对不同的资源,做不同的配置即可。 在webpack.common.js
进行如下配置
webpack.commom.js
const path = require("path")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const { loader } = MiniCssExtractPlugin
module.exports = (env) => {
return {
entry: "./src/index.ts",
output: {
// 把js文件打包docs/js
filename: `js/[name].[hash].js`,
path: path.resolve(__dirname, "docs"),
clean: true,
},
plugins: [
// 把css文件打包docs/css
new MiniCssExtractPlugin({
filename: "css/[name].[hash].css",
}),
],
module: {
rules: [
// 把图片打包到docs/assets
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource",
generator: {
publicPath: "assets/",
outputPath: "assets/",
},
},
// // 把字体打包到docs/assets
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: "asset/resource",
generator: {
publicPath: "assets/",
outputPath: "assets/",
},
},
],
},
}
}
最后会生成如下目录结构:
4. 第三方包打包时,分割成独立的文件
我们可以通过配置optimization.splitChunks
,来分割文件
webpack.common.js
module.exports = (env) => {
return {
optimization: {
runtimeChunk: "single",
splitChunks: {
cacheGroups: {
vendors:
env.mode === "development"
? {}
: {
chunks: "all",
test: /[\\/]node_modules[\\/]/,
name (module) {
if (module.context.includes("node_modules")) {
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1]
return `${packageName.replace("@", "")}`
}
},
},
},
}
}
}
}
至此我们的目标就完成了,下面我们说下怎么部署到github
。
部署
大家都知道github本身是支持部署静态网站的,GitHub Pages
,通过在这里配置,我们就可以有自己的静态网站。配置如下图:
第三步,可以看到这里指定资源是支持两种的root/docs
,我们选择docs
,这就是为啥,我们一开构建打包的时候,没有选择dist
文件夹而是使用docs
,这样设置之后,后面每次该分支上传代码,都会自动构建和部署(可以查看action
)。
总结
至此我们的开发和部署目标就完成了,这个项目目前,本地开发编译有点慢,后续我再优化下,这是我的项目地址,大家有兴趣,可以自己clone
下来 本地玩玩,也可以部署到自己的github
。