繼上一篇 一步步從零開始用 webpack 搭建一個大型項目 以後的第二篇。本文使用了 webpack5
將項目進行了重構,並全程使用的 webpack-chain
來配置 webpack
,每一個功能也都是獨立文件,可單獨使用。所以該項目的配置能夠在任何項目中被使用。此項目可實戰亦可當成 webpack
手冊來學習。我開發這個項目的目的就是不管你是新手仍是有經驗的大佬均可以從中有所收穫。此項目爲想學 webpack
的同窗提供了很好的實戰平臺,每個 插件
每個 loader
都會有詳細的講解及使用背景。javascript
爲了節省你們時間,提高學習效率,我想要將全部 webpack
相關的系列都集成在這裏,這裏的每個優化都是通過我反覆推敲實踐而來,也會吸收一些優秀的開源庫來完善它,此項目將長期維護,也誠摯歡迎全部人蔘與到該項目當中,一塊兒成爲該項目的共建者!css
項目地址:github.com/luoxue-vict…html
由於本文是使用 webpack5
進行了重構,那我就先將第 14 課時放在前面講了前端
本文目錄vue
接下來計劃去作的 TODO github.com/luoxue-vict…java
本章主要將項目升級到 webpack5,先踩一下坑。把踩坑的通過給你們分享一下。node
webpack5 更像是一個黑盒了,好多以前必需要用插件來完成的工做,如今 webpack5 內部已經被集成了,開箱即用。webpack5 主要爲了優化編譯速度、更多的默認配置(內置了更多的配置)、更好的代碼生成、爲未來 webpack 走的更遠作鋪墊。react
本章概要webpack
本教程能夠經過腳手架命令一鍵升級/降級git
webpack-box upgrade 5/4
複製代碼
主要升級了兩個插件,其餘使用到的模塊都已經被兼容,html-webpack-plugin
插件由於涉及到熱更新,目前熱更新的 bug 尚未修復,因此你們切換到 webpack5
以後的第一次編譯能夠成功,可是保存後的再次編譯會報錯(這點我會持續關注,一旦修理當即更新版本)
package.json
{
"html-webpack-plugin": "^4.0.0-beta.11",
"webpack": "^5.0.0-beta.9"
}
複製代碼
webpack-box build index
複製代碼
Version: webpack 4.41.2
如下是使用了 cache-loader
4216ms
2781ms
2827ms
2797ms
Version: webpack 5.0.0-beta.9
使用了 持久緩存
3567ms
2602ms
2609ms
2582ms
能夠看出來 webpack5
使用持久緩存的狀況下比 webpack4
使用 cache-loader 的編譯速度快了 100ms ~ 200ms
,因此之後就沒有必要使用 cache-loader,webpack5 提供了更好的算法跟更優秀的緩存方案
使用持久性緩存時,您再也不須要緩存加載器。與 babel cacheDirectory 相同。
一些錯誤並修復 error
安裝 4.x 版本可修復
npm i html-webpack-plugin@4.0.0-beta.11
複製代碼
未修復
第一次編譯生效,保存以後會報錯,webpack5
對熱更新進行了重寫,致使 html-webpack-plugin
不兼容,緣由可查
可使用 optimization.chunkIds 進行修改
module.exports = {
//...
optimization: {
chunkIds: "named"
}
};
// 鏈式修改
config.optimization.set("chunkIds", "natural");
複製代碼
以下,在 webpack4 中 a、b 都會被打包進 bundle 中,webpack5 會對嵌套的無用代碼也會刪除掉,也就是說 b 並不會被打包進 bundle 中了,由於 b 並無被使用到
// inner.js
export const a = 1;
export const b = 2;
// module.js
import * as inner from "./inner";
export { inner };
// user.js
import * as module from "./module";
console.log(module.inner.a);
複製代碼
webpack5 會檢查都模塊內部的方法是否被使用,若是沒有被使用的話,那麼會把模塊內部調用的方法也會被刪除
可是前提是你要知道這些代碼是無反作用的,否則頗有可能將你的代碼刪掉,好比你要寫一個組件,而你庫裏並無使用它,那麼就有可能在打包的時候被 tree-shaking 了
使用它您須要在 package.json
中配置 "sideEffects": false
,而且設置 optimization.usedExports
爲 true
// package.json
{
"sideEffects": false
}
// config/optimization.js
config.optimization.usedExports(true);
複製代碼
代碼演示
import { something } from "./something";
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}
複製代碼
若是外部模塊沒有使用 test 方法的話,那麼 usingSomething、something 也會在 bundle 中被刪除
告訴 webpack webpack 生成代碼的最大 EcmaScript 版本
webpack4
僅能支持到 ES5,webpack5
支持 ES5 跟 ES6
ecmaVersion
的取值範圍 5 ~ 11 或 2009 ~ 2020,webpack5 默認採用最小值 5
config.output.set("ecmaVersion", 6);
複製代碼
webpack5 能夠根據不一樣類型的文件分別設置 splitChunks 打包的尺寸,默認狀況下只針對 javascript 進行分離打包
config.optimization.splitChunks({
minSize: {
javascript: 30000,
style: 50000
}
});
複製代碼
webpack5 提供了兩種緩存方式,一種是持久緩存將文件緩存在文件系統,另外一種是緩存在內存裏
// type {filesystem | memory}
config.cache({
type: "filesystem"
});
複製代碼
默認狀況下,會被緩存在 node_modules/.cache/webpack
下,您能夠經過 cacheDirectory
選項修改緩存目錄
本章概要
目的:讓職能更加清晰,每一層只作一件事情,使用標準化的 api 去處理同類邏輯
└── api
│── CommandAPI.js
└── PluginAPI.js
└── cwd
│── build:ssr.js
│── build.js
│── dev.js
│── dll.js
│── lint.js
└── ssr:server.js
複製代碼
配置 eslint-loader,在 webpack-box dev 時會檢測 eslint 規則,若是有報錯會顯示在控制檯上
config.module
.rule("eslint")
.pre()
.exclude.add(/node_modules/)
.end()
.test(/\.(vue|(j)sx?)$/)
.use("eslint-loader")
.loader(require.resolve("eslint-loader"))
.options({
extensions,
cache: true,
cacheIdentifier,
emitWarning: allWarnings,
emitError: allErrors,
eslintPath: path.dirname(
resolveModule("eslint/package.json", cwd) ||
resolveModule("eslint/package.json", __dirname)
),
formatter: loadModule("eslint/lib/formatters/codeframe", cwd, true)
});
複製代碼
當咱們項目改變某一個規則時,咱們項目中都會出現大量的錯誤,咱們確定不但願手動一個個去修改,因此咱們須要使用 eslint 的自動修復的功能,它可以幫助咱們修復絕大數的錯誤,還有一些修復不了的咱們再手動修復就能夠了
這裏寫出了部分代碼,更多細節能夠在項目裏面看
packages/eslint/lint.js
const { CLIEngine } = loadModule("eslint", cwd, true) || require("eslint");
const config = Object.assign({
extensions,
fix: true,
cwd
});
const engine = new CLIEngine(config);
const defaultFilesToLint = ["src", "tests", "*.js", ".*.js"].filter(pattern =>
globby
.sync(pattern, { cwd, absolute: true })
.some(p => !engine.isPathIgnored(p))
);
const files = args._ && args._.length ? args._ : defaultFilesToLint;
const report = engine.executeOnFiles(files);
if (config.fix) {
CLIEngine.outputFixes(report);
}
複製代碼
咱們但願經過命令行的形式去修復,webpack-box lint eslint
,因此須要在 cwd
層添加命令行
cwd/lint.js
module.exports = function(injectCommand, api) {
injectCommand(function({ program, cleanArgs, boxConfig }) {
program
.command("lint [type]")
.description("修復lint")
.action(async (name, cmd) => {
const options = cleanArgs(cmd);
const args = Object.assign(options, { name }, boxConfig);
require("../build/lint")(args, api);
});
});
};
複製代碼
這樣咱們可使用 webpack-box lint eslint
去修復大部分的錯誤了,去試一下吧~
固然咱們執行 webpack-box lint eslint
命令時能夠去修復一些錯誤,可是當咱們寫代碼時但願編譯器可以幫助咱們自動修改,而不是等到代碼寫完了纔去校驗,這樣會給咱們帶來二次麻煩,甚至會出現修復不了的問題。
因此咱們使用 vscode
的 eslint
插件來幫助咱們實現吧
首先您必須使用的編譯器是 vscode,固然其它的編譯器也能夠,可是咱們這裏只講 vscode 的配置。
您安裝了 eslint 插件後,須要在設置中設置 "eslint.autoFixOnSave": true
,這樣就能夠在保存時自動修復 eslint
的錯誤了
固然您可能只在這個項目中使用了 eslint
,而在其餘項目中並不須要保存時修復
能夠在根目錄添加
└── .vscode
└── settings.json
複製代碼
放一份我本身的配置供你們參考
{
/*
* @description 編譯器配置
* @param tabSize 默認tab爲兩個空格
* @param formatOnSave 保存時自動修復
*/
"editor.tabSize": 2,
"editor.formatOnSave": true,
/*
* @description eslint 配置
* @param alwaysShowStatus 配置
* @param autoFixOnSave 保存時自動修復
* @param validate 在vue中添加錯誤提示
*/
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
}
],
/*
* @description tslint 配置
* @param autoFixOnSave 保存時自動修復
* @param alwaysShowRuleFailuresAsWarnings 全部特徵都是用 Warnings
*/
"tslint.autoFixOnSave": true,
"tslint.alwaysShowRuleFailuresAsWarnings": true,
/*
* @description stylelint 配置
* @param autoFixOnSave 保存時自動修復
*/
"stylelint.autoFixOnSave": true,
/*
* @description vetur 配置
*/
"vetur.format.defaultFormatter.html": "prettier",
"vetur.format.defaultFormatterOptions": {
"prettier": {
"semi": false,
"singleQuote": true
}
},
/*
* @description 配置編輯器設置以覆蓋某種語言
*/
"[typescript]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "eg2.tslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "eg2.tslint"
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
複製代碼
上述的操做都是咱們理想狀態下的檢測跟修復,可是有時還會遇到意外的狀況,並無 lint 代碼就提交了,這樣會致使可能出現問題,因此咱們須要在提交代碼前進行一次代碼檢驗
在 package.json 中添加 lint-staged,在代碼提交時會先執行 lint,lint 經過以後才能提交成功
package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
複製代碼
本章概要
使用 stylelint-config-standard 插件
.stylelintrc.js
module.exports = {
root: true,
extends: "stylelint-config-standard"
};
複製代碼
module.exports = ({
config,
options: { stylelint: { lintOnSave = false, extensions } = {} },
api
}) => {
const StyleLintPlugin = require("stylelint-webpack-plugin");
const CodeframeFormatter = require("stylelint-codeframe-formatter");
const stylelint = [];
return () => {
config.plugin("stylelint").use(StyleLintPlugin, [
Object.assign(
{
failOnError: lintOnSave === "error",
files: ["src/**/*.{vue,htm,html,css,sss,less,scss}"],
formatter: CodeframeFormatter
},
stylelint
)
]);
};
};
複製代碼
module.exports = async function lint({ api, args = {}, pluginOptions = {} }) {
const cwd = api.resolve(".");
const files =
args._ && args._.length
? args._
: [cwd + "/src/**/*.{vue,htm,html,css,sss,less,scss}"];
if (args["no-fix"]) {
args.fix = false;
delete args["no-fix"];
}
const { formatter } = args;
if (
formatter &&
typeof formatter === "string" &&
!["json", "string", "verbose"].includes(formatter)
) {
try {
args.formatter = require(formatter);
} catch (e) {
delete args.formatter;
if (typeof pluginOptions.formatter !== "function") {
console.log(
format(
chalk`{bgYellow.black WARN }`,
chalk`${e.toString()}\n{yellow Invalid formatter}`
)
);
}
}
}
const options = Object.assign(
{},
{
configBasedir: cwd,
fix: true,
files,
formatter: CodeframeFormatter
},
pluginOptions,
normalizeConfig(args)
);
try {
const { errored, results, output: formattedOutput } = await stylelint.lint(
options
);
if (!errored) {
if (!args.silent) {
const hasWarnings = results.some(result => {
if (result.ignored) {
return null;
}
return result.warnings.some(
warning => warning.severity === "warning"
);
});
if (hasWarnings) {
console.log(formattedOutput);
} else {
console.log(
format(
chalk`{bgGreen.black DONE }`,
`stylelint 沒有發現錯誤!${ options.fix ? chalk` {blue (已經自動修復)}` : "" }`
)
);
}
}
} else {
console.log(formattedOutput);
process.exit(1);
}
} catch (err) {
console.log(
format(chalk`{bgRed.black ERROR }`, err.stack.slice(" Error:".length))
);
process.exit(1);
}
};
複製代碼
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{vue,htm,html,css,sss,less,scss}": [
"webpack-box lint stylelint",
"git add"
],
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
複製代碼
本章概要
config/tslintPlugin.js
module.exports = ({
config,
options: { tslint: { lintOnSave = false, useThreads = false } = {} },
api
}) => {
const fs = require("fs");
return () => {
config.plugin("fork-ts-checker").tap(([options]) => {
options.tslint =
lintOnSave !== false && fs.existsSync(api.resolve("tslint.json"));
options.formatter = "codeframe";
options.checkSyntacticErrors = useThreads;
return [options];
});
};
};
複製代碼
tslint.json
{
"defaultSeverity": "warning",
"extends": ["tslint:recommended"],
"linterOptions": {
"exclude": ["node_modules/**"]
},
"rules": {
"max-classes-per-file": [true, 5, "exclude-class-expressions"],
"quotemark": [true, "single"],
"semicolon": [true, "never"],
"indent": [true, "spaces", 2],
"ordered-imports": false,
"object-literal-sort-keys": false,
"no-consecutive-blank-lines": false,
"disable-next-line": false,
"only-arrow-functions": false,
"radix": false,
"class-name": false,
"eofline": false,
"no-unused-expression": false,
"no-console": false,
"trailing-comma": false,
"interface-name": false
}
}
複製代碼
const { done } = require("@vue/cli-shared-utils");
module.exports = function lint({ args = {}, api, silent }) {
const options = {
fix: args.fix !== false,
formatter: args.format || "codeFrame",
formattersDirectory: args["formatters-dir"],
rulesDirectory: args["rules-dir"]
};
const program = tslint.Linter.createProgram(api.resolve("tsconfig.json"));
const linter = new tslint.Linter(options, program);
const updateProgram = linter.updateProgram;
linter.updateProgram = function(...args) {
updateProgram.call(this, ...args);
patchProgram(this.program);
};
const tslintConfigPath = tslint.Configuration.CONFIG_FILENAMES.map(filename =>
api.resolve(filename)
).find(file => fs.existsSync(file));
const config = tslint.Configuration.findConfiguration(tslintConfigPath)
.results;
const lint = file => {
const filePath = api.resolve(file);
const isVue = isVueFile(file);
patchWriteFile();
linter.lint(filePath, "", isVue ? vueConfig : config);
restoreWriteFile();
};
const files =
args._ && args._.length
? args._
: [cwd + "/src/**/*.ts", cwd + "/src/**/*.vue", cwd + "/src/**/*.tsx"];
return globby(files, { cwd }).then(files => {
files.forEach(lint);
if (silent) return;
const result = linter.getResult();
if (result.output.trim()) {
process.stdout.write(result.output);
} else if (result.fixes.length) {
const f = new tslint.Formatters.ProseFormatter();
process.stdout.write(f.format(result.failures, result.fixes));
} else if (!result.failures.length) {
done("tslint 沒有發現錯誤.\n");
}
if (result.failures.length && !args.force) {
process.exitCode = 1;
}
});
};
複製代碼
{
"lint-staged": {
"*.{vue,htm,html,css,sss,less,scss}": [
"webpack-box lint stylelint",
"git add"
],
"*.{ts,tsx}": ["webpack-box lint tslint", "git add"],
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
複製代碼
在咱們工做中,若是一個文件須要被 copy 到另一個目錄下,那麼這個文件的引用依賴就可能發生路徑錯誤。還有咱們不喜歡每次引入依賴都要逐層去查找,咱們但願可以有一個別名來指定某一個目錄,不管咱們在哪裏使用它。
本章概要
src/main.js
import { cube } from "./treeShaking";
import { cube } from "@/treeShaking";
import { cube } from "@src/treeShaking";
複製代碼
alias: {
'@': resolve('src'),
'@src': resolve('src')
}
複製代碼
module.exports = ({ config, options, resolve }) => {
const fs = require("fs");
const conf = options.alias;
return () => {
// 生成默認別名
const dirs = fs.readdirSync(resolve("src"));
let aliasConfig = config.resolve.extensions
.merge([".mjs", ".js", ".jsx", ".vue", ".ts", ".json", ".wasm"])
.end().alias;
dirs.forEach(v => {
const stat = fs.statSync(resolve(`src/${v}`));
if (stat.isDirectory()) {
aliasConfig = aliasConfig.set(`@${v}`, resolve(`src/${v}`));
}
});
// 用戶配置別名
if (conf.alias) {
const keys = Object.keys(conf.alias);
keys.forEach(key => {
aliasConfig = aliasConfig.set(key, conf.alias[key]);
});
}
// 自定義設置別名
aliasConfig.set("@", resolve("src")).set("@src", resolve("src"));
};
};
複製代碼
若是您使用的是 ts 的話,那麼配置別名了以後會失去類型,提示找不到模塊,因此咱們須要在編譯器配置對應的別名才能夠
tsconfig.json/jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"rootDir": ".",
"paths": {
"@src/*": [
"src/*"
],
"@/*": [
"src/*"
],
}
}
}
複製代碼
有時咱們須要在腳手架跟業務代碼之間有一個通訊的橋樑
好比咱們 npm run build
時咱們是運行的生產環境,我想在 main.js
中生產環境下作一些特殊的邏輯。可是 main.js
執行是在瀏覽器端,而 npm run build
時運行在 node
端,兩端沒有辦法作通信。那麼咱們怎麼辦呢?
webpack
給咱們提供了一個插件 EnvironmentPlugin
,這個插件能夠將咱們在 node
端定義的變量,在編譯時將值編譯到代碼中,舉個例子
咱們在 main.js
中寫了一段 node
中看起來很常見的代碼,可是這在瀏覽器中是不能識別的,由於瀏覽器中並無 process
對象,這段代碼不出意外會報錯
main.js
if (process.env.NODE_ENV === "production") {
console.log("Welcome to production");
}
複製代碼
咱們配置 webpack.EnvironmentPlugin
插件
const webpack = require("webpack");
module.exports = ({ config, resolve, options }) => {
return () => {
const resolveClientEnv = require("../util/resolveClientEnv");
config
.plugin("process-env")
.use(webpack.EnvironmentPlugin, [resolveClientEnv(options)]);
};
};
複製代碼
util/resolveClientEnv.js
module.exports = function resolveClientEnv(options, raw) {
const env = {};
if (process.env) {
Object.keys(process.env).forEach(key => {
if (key === "NODE_ENV") {
env[key] = process.env[key];
}
});
}
if (options.env) {
Object.assign(env, options.env);
}
return env;
};
複製代碼
咱們執行 npm run build
,看一下 dist/index.bundle.js
會編譯成什麼
// "production" === "production"
if (true) {
console.log("Welcome to production");
}
複製代碼
webpack
將 process.env.NODE_ENV
的值編譯在 bundle
中,這樣咱們就能夠在 web
端運行了,並且編譯出來是在生產環境下
有時咱們常常會出現這樣的狀況,明明本地編譯沒有問題,可是上線 jenkins 編譯的時候就會報錯,這種問題每每會花費咱們較長的時間才能發現這個 bug,原來是本地路徑的大小寫出現了問題,引用路徑時咱們本地是不區分大小寫的。舉個例子
└──── src
│── Index.js
└── main.js
複製代碼
上面的路徑中 Index.js 的首字母是大寫,可是我在 main.js 用小寫去引用它
main.js
import Index from "./index.js";
複製代碼
這樣在本地是不會報錯的,可是當你用 Jenkins 上線的時候,就會報錯找不到 ./index.js 模塊
因此咱們須要一個插件,在咱們開發時就嚴格檢查大小寫,這樣就不會出現這樣的問題了。
咱們使用 case-sensitive-paths-webpack-plugin
插件來實現它
module.exports = ({ config, webpackVersion, resolve, options }) => {
return () => {
// webpack 5 不兼容
if (parseInt(webpackVersion) >= 5) return;
config
.plugin("case-sensitive-paths")
.use(require("case-sensitive-paths-webpack-plugin"));
};
};
複製代碼
這章就直接上代碼吧,是以前基礎篇的補充
module.exports = ({ config, webpackVersion, resolve, options }) => {
return () => {
const getAssetPath = require("../util/getAssetPath");
const inlineLimit = 4096;
const genAssetSubPath = dir => {
return getAssetPath(
options,
`${dir}/[name]${options.filenameHashing ? ".[hash:8]" : ""}.[ext]`
);
};
const genUrlLoaderOptions = dir => {
return {
limit: inlineLimit,
fallback: {
loader: "file-loader",
options: {
name: genAssetSubPath(dir)
}
}
};
};
config.module
.rule("images")
.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("img"));
config.module
.rule("svg")
.test(/\.(svg)(\?.*)?$/)
.use("file-loader")
.loader(require.resolve("file-loader"))
.options({
name: genAssetSubPath("img")
});
config.module
.rule("media")
.test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("media"));
config.module
.rule("fonts")
.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("fonts"));
};
};
複製代碼
在書寫 css 時,咱們會將經常使用到的函數/變量等封裝成一個 global.less/scss,而後在咱們用到的時候將其引入。顯然每次都要手動引入變得很麻煩,也容易出錯(尤爲組內來新人的時候),因此咱們想若是把 global 自動引入到文件中不就完美了嗎?
咱們須要一個 style-resources-loader
來幫助咱們完成這件事
config/styleResourceLoader.js
module.exports = ({ config, options }) => {
const resourcesOpt = options.resources;
return () => {
["normal"].forEach(oneOf => {
Object.keys(resourcesOpt).forEach(loader => {
config.module
.rule(loader)
.oneOf(oneOf)
.use("style-resources-loader")
.loader("style-resources-loader")
.options({
patterns: resourcesOpt[loader].patterns
});
});
});
};
};
複製代碼
config/style.js
if (loader) {
let resolvedLoader;
try {
resolvedLoader = require.resolve(loader);
} catch (error) {
resolvedLoader = loader;
}
rule
.use(loader)
.loader(resolvedLoader)
// options 是對應 config 中的 css 參數,能夠自行配置對應loader的參數
.options(Object.assign({ sourceMap }, options));
}
複製代碼
box.config.js
{
"css": {
"sourceMap": true, // 是否開啓css source map
"loaderOptions": { // 配置loader的options
"css": {},
"less": {
"globalVars": { // less 設置全局變量
"gray": "#ccc"
}
},
"sass": {},
"postcss": {},
"stylus": {}
},
"isCssModule": false, // 是否對css進行模塊化處理
"needInlineMinification": false // 是否須要壓縮css
},
"resources": {
"less": {
"patterns": [path.resolve(__dirname, "./src/global/*.less")]
},
"scss": {
"patterns": [path.resolve(__dirname, "./src/global/*.scss")]
}
}
}
複製代碼
└──── src
│── global
│ │── index.less
│ └── index.scss
└── style
└── index.less
複製代碼
global/index.less
.g-less-height () {
height: 100%;
}
.g-less-test {
width: 100%;
}
複製代碼
style/index.less
.test {
width: 300px;
color: @gray;
.g-less-height();
}
複製代碼
style/index.scss
.g-scss-test {
width: 100%;
}
複製代碼
dist/css/index.css
.g-less-test {
width: 100%;
}
.test {
color: #ccc;
width: 100%;
height: 100%;
}
.g-scss-test {
width: 100%;
}
複製代碼
可見全局的樣式都被打包進 dist/css/index.css
中了,咱們不再用每次都手動引入了
放在根目錄,開啓自動修復 eslint/tslint/stylelint 功能
.vscode/setting.json
{
/* * @description 編譯器配置 * @param tabSize 默認tab爲兩個空格 * @param formatOnSave 保存時自動修復 */
"editor.tabSize": 2,
"editor.formatOnSave": true,
/* * @description eslint 配置 * @param alwaysShowStatus 配置 * @param autoFixOnSave 保存時自動修復 * @param validate 在vue中添加錯誤提示 */
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
}
],
/* * @description tslint 配置 * @param autoFixOnSave 保存時自動修復 * @param alwaysShowRuleFailuresAsWarnings 全部特徵都是用 Warnings */
"tslint.autoFixOnSave": true,
"tslint.alwaysShowRuleFailuresAsWarnings": true,
/* * @description stylelint 配置 * @param autoFixOnSave 保存時自動修復 */
"stylelint.autoFixOnSave": true,
/* * @description vetur 配置 */
"vetur.format.defaultFormatter.html": "prettier",
"vetur.format.defaultFormatterOptions": {
"prettier": {
"semi": false,
"singleQuote": true
}
},
/* * @description 配置編輯器設置以覆蓋某種語言 */
"[typescript]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "eg2.tslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "eg2.tslint"
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
複製代碼
配合 lint 作一些,代碼樣式上的格式化
prettier.config.js
/** * pretiier 標準配置 */
module.exports = {
// 在ES5中有效的結尾逗號(對象,數組等)
trailingComma: "es5",
// 不使用縮進符,而使用空格
useTabs: false,
// tab 用兩個空格代替
tabWidth: 2,
// 僅在語法可能出現錯誤的時候纔會添加分號
semi: false,
// 使用單引號
singleQuote: true,
// 在Vue文件中縮進腳本和樣式標籤。
vueIndentScriptAndStyle: true,
// 一行最多 100 字符
printWidth: 100,
// 對象的 key 僅在必要時用引號
quoteProps: "as-needed",
// jsx 不使用單引號,而使用雙引號
jsxSingleQuote: false,
// 大括號內的首尾須要空格
bracketSpacing: true,
// jsx 標籤的反尖括號須要換行
jsxBracketSameLine: false,
// 箭頭函數,只有一個參數的時候,也須要括號
arrowParens: "always",
// 每一個文件格式化的範圍是文件的所有內容
rangeStart: 0,
rangeEnd: Infinity,
// 不須要寫文件開頭的 @prettier
requirePragma: false,
// 不須要自動在文件開頭插入 @prettier
insertPragma: false,
// 使用默認的折行標準
proseWrap: "preserve",
// 根據顯示樣式決定 html 要不要折行
htmlWhitespaceSensitivity: "css",
// 換行符使用 lf
endOfLine: "lf"
};
複製代碼
至此webpack系列2已經結束了,更精彩的還在後面,前面兩篇文章只能算是爲後來的大項目作鋪墊,後續我會使用lerna進行重構,將使用插件化管理,構建插件化生態令人人有益。後面會構建vue跟react項目,甚至小程序,跨端等都會被集成進來。
您能夠將您想的想法提到這裏面 github.com/luoxue-vict… 我會認真對待每個issue
最後感謝你們的支持!我叫技匠
能夠微信公衆號搜索前端技匠
,也能夠加我微信 luoxue2479
進羣交流。