欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

目錄

柚子快報(bào)激活碼778899分享:webpack learn

柚子快報(bào)激活碼778899分享:webpack learn

http://yzkb.51969.com/

chunkModules: false

}) + ‘\n\n’

);

if (stats.hasErrors()) {

console.log(chalk.red(‘構(gòu)建失敗\n’));

process.exit(1);

}

console.log(chalk.cyan(‘build完成\n’));

});

構(gòu)建開(kāi)發(fā)環(huán)境(devServer)

build/dev.js

const config = require(‘./base’)();

const webpack = require(‘webpack’);

const chalk = require(‘chalk’);

const WebpackDevServer = require(‘webpack-dev-server’);

const port = 8080;

const publicPath = ‘/common/’;

config.devServer

.quiet(true)

.hot(true)

.https(false)

.disableHostCheck(true)

.publicPath(publicPath)

.clientLogLevel(‘none’);

const compiler = webpack(config.toConfig());

// 拿到 devServer 參數(shù)

const chainDevServer = compiler.options.devServer;

const server = new WebpackDevServer(

compiler,

Object.assign(chainDevServer, {})

);

[‘SIGINT’, ‘SIGTERM’].forEach(signal => {

process.on(signal, () => {

server.close(() => {

process.exit(0);

});

});

});

// 監(jiān)聽(tīng)端口

server.listen(port);

new Promise(() => {

compiler.hooks.done.tap(‘dev’, stats => {

const empty = ’ ';

const common = `App running at:

Local: http://127.0.0.1:

p

o

r

t

{port}

port{publicPath}\n`;

console.log(chalk.cyan(‘\n’ + empty + common));

});

});

提取 css

config/css.js

css 提取 loader 配置

module.exports = (config, resolve) => {

return (lang, test) => {

const baseRule = config.module.rule(lang).test(test);

const normalRule = baseRule.oneOf(‘normal’);

applyLoaders(normalRule);

function applyLoaders(rule) {

rule

.use(‘extract-css-loader’)

.loader(require(‘mini-css-extract-plugin’).loader)

.options({

publicPath: ‘./’

});

rule

.use(‘css-loader’)

.loader(‘css-loader’)

.options({});

}

};

};

css 提取插件 MiniCssExtractPlugin

config/MiniCssExtractPlugin.js

const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);

module.exports = (config, resolve) => {

return () => {

config

.oneOf(‘normal’)

.plugin(‘mini-css-extract’)

.use(MiniCssExtractPlugin);

};

};

自動(dòng)生成 html

config/HtmlWebpackPlugin.js

const HtmlWebpackPlugin = require(‘html-webpack-plugin’);

module.exports = (config, resolve) => {

return () => {

config.plugin(‘html’).use(HtmlWebpackPlugin, [

{

template: ‘public/index.html’

}

]);

};

};

項(xiàng)目測(cè)試

測(cè)試 html 模板

public/index.html

learn_webpack

測(cè)試 css 模板

src/style/index.css

.test {

width: 200px;

height: 200px;

color: red;

background-color: orange;

}

程序入口

src/main.js

require(‘./style/index.css’);

const h2 = document.createElement(‘h2’);

h2.className = ‘test’;

h2.innerText = ‘test’;

document.body.append(h2);

課題 3:基礎(chǔ)配置之loader

本章提要:

配置 babel 使用 babel 配置 ts ts 靜態(tài)類(lèi)型檢查 友好錯(cuò)誤提示插件 配置樣式,style,css、less、sass、postcss 等 postcss 配置 編譯前后 css 對(duì)比 配置 autoprefixer 開(kāi)啟 source map

目錄

增加以下文件

│──── config // 配置目錄

│ │── babelLoader.js // babel-loader 配置

│ │── ForkTsChecker.js // ts 靜態(tài)檢查

│ │── FriendlyErrorsWebpackPlugin.js // 友好錯(cuò)誤提示

│ └── style

│──── src // 開(kāi)發(fā)目錄

│ │── style

│ │ │── app.css

│ │ │── index.less // 測(cè)試 less

│ │ │── index.scss // 測(cè)試 sass

│ │ └── index.postcss // 測(cè)試 postcss

│ └── ts

│ └── index.ts // 測(cè)試 ts

│── babel.js

│── postcss.config.js // postcss 配置

│── tsconfig.json // ts 配置

└──── dist // 打包后的目錄

│── app.bundle.js

│── app.css

└── index.html

配置 babel

config/babelLoader.js

module.exports = (config, resolve) => {

const baseRule = config.module.rule(‘js’).test(/.js│.tsx?$/);

const babelPath = resolve(‘babel.js’);

const babelConf = require(babelPath);

const version = require(resolve(‘node_modules/@babel/core/package.json’))

.version;

return () => {

baseRule

.use(‘babel’)

.loader(require.resolve(‘babel-loader’))

.options(babelConf({ version }));

};

};

使用 babel 配置 ts

這里我們使用?babel?插件?@babel/preset-typescript?將?ts?轉(zhuǎn)成?js,并使用?ForkTsCheckerWebpackPlugin、ForkTsCheckerNotifierWebpackPlugin?插件進(jìn)行錯(cuò)誤提示。

babel.js

module.exports = function(api) {

return {

presets: [

[

‘@babel/preset-env’,

{

targets: {

chrome: 59,

edge: 13,

firefox: 50,

safari: 8

}

}

],

[

‘@babel/preset-typescript’,

{

allExtensions: true

}

]

],

plugins: [

‘@babel/plugin-transform-typescript’,

‘transform-class-properties’,

‘@babel/proposal-object-rest-spread’

]

};

};

ts 靜態(tài)類(lèi)型檢查

const ForkTsCheckerWebpackPlugin = require(‘fork-ts-checker-webpack-plugin’);

const ForkTsCheckerNotifierWebpackPlugin = require(‘fork-ts-checker-notifier-webpack-plugin’);

module.exports = (config, resolve) => {

return () => {

config.plugin(‘ts-fork’).use(ForkTsCheckerWebpackPlugin, [

{

// 將async設(shè)為false,可以阻止Webpack的emit以等待類(lèi)型檢查器/linter,并向Webpack的編譯添加錯(cuò)誤。

async: false

}

]);

// 將TypeScript類(lèi)型檢查錯(cuò)誤以彈框提示

// 如果fork-ts-checker-webpack-plugin的async為false時(shí)可以不用

// 否則建議使用,以方便發(fā)現(xiàn)錯(cuò)誤

config.plugin(‘ts-notifier’).use(ForkTsCheckerNotifierWebpackPlugin, [

{

title: ‘TypeScript’,

excludeWarnings: true,

skipSuccessful: true

}

]);

};

};

友好錯(cuò)誤提示插件

config/FriendlyErrorsWebpackPlugin.js

const FriendlyErrorsWebpackPlugin = require(‘friendly-errors-webpack-plugin’);

module.exports = (config, resolve) => {

return () => {

config.plugin(‘error’).use(FriendlyErrorsWebpackPlugin);

};

};

配置樣式,style,css、less、sass、postcss 等

module.exports = (config, resolve) => {

const createCSSRule = (lang, test, loader, options = {}) => {

const baseRule = config.module.rule(lang).test(test);

const normalRule = baseRule.oneOf(‘normal’);

normalRule

.use(‘extract-css-loader’)

.loader(require(‘mini-css-extract-plugin’).loader)

.options({

hmr: process.env.NODE_ENV === ‘development’,

publicPath: ‘/’

});

normalRule

.use(‘css-loader’)

.loader(require.resolve(‘css-loader’))

.options({});

normalRule.use(‘postcss-loader’).loader(require.resolve(‘postcss-loader’));

if (loader) {

const rs = require.resolve(loader);

normalRule

.use(loader)

.loader(rs)

.options(options);

}

};

return () => {

createCSSRule(‘css’, /.css$/, ‘css-loader’, {});

createCSSRule(‘less’, /.less$/, ‘less-loader’, {});

createCSSRule(‘scss’, /.scss$/, ‘sass-loader’, {});

createCSSRule(‘postcss’, /.p(ost)?css$/);

};

};

postcss 配置

module.exports = {

plugins: {

‘postcss-px-to-viewport’: {

unitToConvert: ‘px’,

viewportWidth: 750,

unitPrecision: 5,

propList: [‘*’],

viewportUnit: ‘vw’,

fontViewportUnit: ‘vw’,

selectorBlackList: [],

minPixelValue: 1,

mediaQuery: false,

replace: true,

exclude: [],

landscape: false,

landscapeUnit: ‘vw’,

landscapeWidth: 568

}

}

};

編譯前后 css 對(duì)比

src/style/index.less

/* index.less */

.test {

width: 300px;

}

dist/app.css

/* index.css */

.test {

width: 36.66667vw;

height: 26.66667vw;

color: red;

background-color: orange;

}

/* app.css */

.test {

font-size: 8vw;

}

/* index.less */

.test {

width: 40vw;

}

/* index.scss */

.test {

height: 40vw;

}

/* index.postcss */

.test {

background: green;

height: 26.66667vw;

}

配置 autoprefixer

自動(dòng)添加 css 前綴

postcss.config.js

module.exports = {

plugins: {

autoprefixer: {

overrideBrowserslist: [

‘> 1%’,

‘last 3 versions’,

‘iOS >= 8’,

‘Android >= 4’,

‘Chrome >= 40’

]

}

}

};

轉(zhuǎn)換前

/* index.css */

.test {

width: 200px;

height: 200px;

color: red;

display: flex;

background-color: orange;

}

轉(zhuǎn)換后

/* index.css */

.test {

width: 26.66667vw;

height: 26.66667vw;

color: red;

display: -webkit-box;

display: -webkit-flex;

display: -ms-flexbox;

display: flex;

background-color: orange;

}

開(kāi)啟 source map

config.devtool(‘cheap-source-map’);

└── dist

│── app.bundle.js

│── app.bundle.js.map

│── app.css

│── app.css.map

└── index.html

在源文件下會(huì)有一行注釋?zhuān)C明開(kāi)啟了 sourcemap

/# sourceMappingURL=app.css.map/

課時(shí) 4:webpack性能優(yōu)化

本章講解

分離 Manifest Code Splitting(代碼分割) Bundle Splitting(打包分割) Tree Shaking(刪除死代碼) 開(kāi)啟 gzip

分離 Manifest

module.exports = (config, resolve) => {

return () => {

config

.optimization

.runtimeChunk({

name: “manifest”

})

}

}

Code Splitting

使用動(dòng)態(tài) import 或者 require.ensure 語(yǔ)法,在第一節(jié)已經(jīng)講解 使用?babel-plugin-import?插件按需引入一些組件庫(kù)

Bundle Splitting

將公共的包提取到?chunk-vendors?里面,比如你require(‘vue’),webpack 會(huì)將 vue 打包進(jìn) chunk-vendors.bundle.js

module.exports = (config, resolve) => {

return () => {

config

.optimization.splitChunks({

chunks: ‘a(chǎn)sync’,

minSize: 30000,

minChunks: 1,

maxAsyncRequests: 3,

maxInitialRequests: 3,

cacheGroups: {

vendors: {

name: chunk-vendors,

test: /[\/]node_modules[\/]/,

priority: -10,

chunks: ‘initial’

},

common: {

name: chunk-common,

minChunks: 2,

priority: -20,

chunks: ‘initial’,

reuseExistingChunk: true

}

}

})

config.optimization.usedExports(true)

}

}

Tree Shaking

config/optimization.js

config.optimization.usedExports(true);

src/treeShaking.js

export function square(x) {

return x * x;

}

export function cube(x) {

return x * x * x;

}

在 main.js 中只引用了 cube

import { cube } from ‘./treeShaking’;

console.log(cube(2));

未使用 Tree Shaking

{

“./src/treeShaking.js”: function(

module,

webpack_exports,

webpack_require

) {

“use strict”;

webpack_require.r(webpack_exports);

webpack_require.d(webpack_exports, “square”, function() {

return square;

});

webpack_require.d(webpack_exports, “cube”, function() {

return cube;

});

function square(x) {

return x * x;

}

function cube(x) {

return x * x * x;

}

}

}

使用了 Tree Shaking

這里只導(dǎo)出了 cube 函數(shù),并沒(méi)有將 square 導(dǎo)出去

當(dāng)然你可以看見(jiàn) square 函數(shù)還是在 bundle 里面,但是在壓縮的時(shí)候就會(huì)被干掉了,因?yàn)樗](méi)有被引用

{

“./src/treeShaking.js”: function(

module,

webpack_exports,

webpack_require

) {

“use strict”;

webpack_require.d(webpack_exports, “a”, function() {

return cube;

});

function square(x) {

return x * x;

}

function cube(x) {

return x * x * x;

}

}

}

只有當(dāng)函數(shù)給定輸入后,產(chǎn)生相應(yīng)的輸出,且不修改任何外部的東西,才可以安全做shaking的操作

如何使用tree-shaking?

確保代碼是es6格式,即 export,import package.json中,設(shè)置 sideEffects 確保 tree-shaking 的函數(shù)沒(méi)有副作用 babelrc中設(shè)置presets [[“@babel/preset-env”, { “modules”: false }]] 禁止轉(zhuǎn)換模塊,交由webpack進(jìn)行模塊化處理 結(jié)合uglifyjs-webpack-plugin

其實(shí)在?webpack4?我們根本不需要做這些操作了,因?yàn)?webpack?在生產(chǎn)環(huán)境已經(jīng)幫我們默認(rèn)添加好了,開(kāi)箱即用!

開(kāi)啟 gzip

CompressionWebpackPlugin.js

const CompressionWebpackPlugin = require(‘compression-webpack-plugin’);

module.exports = (config, resolve) => {

return () => {

config.plugin(‘CompressionWebpackPlugin’).use(CompressionWebpackPlugin, [

{

algorithm: ‘gzip’,

test: /.js(?.*)?$/i,

threshold: 10240,

minRatio: 0.8

}

]);

};

};

課時(shí) 5:手寫(xiě)loader實(shí)現(xiàn)可選鏈

本章內(nèi)容

什么是 webpack loader 可選鏈介紹 loader 實(shí)現(xiàn)可選鏈

什么是 webpack loader

webpack loader?是?webpack?為了處理各種類(lèi)型文件的一個(gè)中間層,webpack?本質(zhì)上就是一個(gè)?node?模塊,它不能處理?js?以外的文件,那么?loader?就幫助?webpack?做了一層轉(zhuǎn)換,將所有文件都轉(zhuǎn)成字符串,你可以對(duì)字符串進(jìn)行任意操作/修改,然后返回給?webpack?一個(gè)包含這個(gè)字符串的對(duì)象,讓?webpack?進(jìn)行后面的處理。如果把?webpack?當(dāng)成一個(gè)垃圾工廠的話(huà),那么?loader?就是這個(gè)工廠的垃圾分類(lèi)!

可選鏈介紹

這里并不是純粹意義上的可選鏈,因?yàn)?babel?跟?ts?都已經(jīng)支持了,我們也沒(méi)有必要去寫(xiě)一個(gè)完整的可選鏈,只是來(lái)加深一下對(duì)?loader?的理解,?loader?在工作當(dāng)中能幫助我們做什么?

用途?當(dāng)我們?cè)L問(wèn)一個(gè)對(duì)象屬性時(shí)不必?fù)?dān)心這個(gè)對(duì)象是?undefined?而報(bào)錯(cuò),導(dǎo)致程序不能繼續(xù)向下執(zhí)行

解釋?在???之前的所有訪問(wèn)鏈路都是合法的,不會(huì)產(chǎn)生報(bào)錯(cuò)

const obj = {

foo: {

bar: {

baz: 2

}

}

}

console.log(obj.foo.bar?.baz) // 2

// 被轉(zhuǎn)成 obj && obj.foo && obj.foo.bar && obj.foo.bar.baz

console.log(obj.foo.err?.baz) // undefined

// 被轉(zhuǎn)成 obj && obj.foo && obj.foo.err && obj.foo.err.baz

loader 實(shí)現(xiàn)可選鏈

配置loader,options-chain-loader

config/OptionsChainLoader.js

module.exports = (config, resolve) => {

const baseRule = config.module.rule(‘js’).test(/.js|.tsx?$/);

const normalRule = baseRule.oneOf(‘normal’);

return () => {

normalRule

.use(‘options-chain’)

.loader(resolve(‘options-chain-loader’))

}

}

其實(shí)就是正則替換,loader?將整個(gè)文件全部轉(zhuǎn)換成字符串,content?就是整個(gè)文件的內(nèi)容,對(duì)?content?進(jìn)行修改,修改完成后再返回一個(gè)新的?content?就完成了一個(gè)?loader?轉(zhuǎn)換。是不是很簡(jiǎn)單?

下面的操作意思就是,我們匹配?obj.foo.bar?.?并把它轉(zhuǎn)成?obj && obj.foo && obj.foo.bar && obj.foo.bar.

options-chain-loader.js

module.exports = function(content) {

return content.replace(new RegExp(/([$_\w.]+?.)/,‘g’),function(res) {

let str = res.replace(/?./,‘’);

let arrs = str.split(‘.’);

let strArr = [];

for(let i = 1; i <= arrs.length; i++) {

strArr.push(arrs.slice(0,i).join(‘.’));

}

let compile = strArr.join(‘&&’);

const done = compile + ‘&&’ + str + ‘.’

return done;

});

};

課時(shí) 6:webpack編譯優(yōu)化

本章內(nèi)容

cache-loader DllPlugin threadLoader

cache-loader

cache-loader?主要是將打包好的文件緩存在硬盤(pán)的一個(gè)目錄里,一般存在?node_modules/.cache?下,當(dāng)你再次?build?的時(shí)候如果此文件沒(méi)有修改就會(huì)從緩存中讀取已經(jīng)編譯過(guò)的文件,只有有改動(dòng)的才會(huì)被編譯,這樣就大大降低了編譯的時(shí)間。尤其是項(xiàng)目越大時(shí)越明顯。

此項(xiàng)目使用前后數(shù)據(jù)對(duì)比 3342ms --> 2432ms 效果還是比較明顯

這里只對(duì) babel 加入了 cache-loader,因?yàn)槲覀兊?ts/js 都是由 babel 進(jìn)行編譯的,不需要對(duì) ts-loader 緩存(我們也沒(méi)有用到)

config/cacheLoader.js

module.exports = (config, resolve) => {

const baseRule = config.module.rule(‘js’).test(/.js|.tsx?$/);

const babelPath = resolve(‘babel.js’)

const babelConf = require(babelPath);

const version = require(resolve(‘node_modules/@babel/core/package.json’)).version

return () => {

baseRule

.exclude

.add(filepath => {

// 不緩存 node_modules 下的文件

return /node_modules/.test(filepath)

})

.end()

.use(‘cache-loader’)

.loader(‘cache-loader’)

.options({

// 緩存位置

cacheDirectory: resolve(‘node_modules/.cache/babel’)

})

}

}

DllPlugin

DllPlugin 是將第三方長(zhǎng)期不變的包與實(shí)際項(xiàng)目隔離開(kāi)來(lái)并分別打包,當(dāng)我們 build 時(shí)再將已經(jīng)打包好的 dll 包引進(jìn)來(lái)就 ok 了

我提取了兩個(gè)包 vue、react,速度差不多提升了 200ms,從 2698ms 到 2377ms

打包 dll

build/dll.js

const path = require(“path”);

const dllPath = path.join(process.cwd(), ‘dll’);

const Config = require(‘webpack-chain’);

const config = new Config();

const webpack = require(‘webpack’)

const rimraf = require(‘rimraf’);

const ora = require(‘ora’)

const chalk = require(‘chalk’)

const BundleAnalyzerPlugin = require(‘…/config/BundleAnalyzerPlugin’)(config)

BundleAnalyzerPlugin()

config

.entry(‘dll’)

.add(‘vue’)

.add(‘react’)

.end()

.set(‘mode’, “production”)

.output

.path(dllPath)

.filename(‘[name].js’)

.library(“[name]”)

.end()

.plugin(‘DllPlugin’)

.use(webpack.DllPlugin, [{

name: “[name]”,

path: path.join(process.cwd(), ‘dll’, ‘manifest.json’),

}])

.end()

rimraf.sync(path.join(process.cwd(), ‘dll’))

const spinner = ora(‘開(kāi)始構(gòu)建項(xiàng)目…’)

spinner.start()

webpack(config.toConfig(), function (err, stats) {

spinner.stop()

if (err) throw err

process.stdout.write(stats.toString({

colors: true,

modules: false,

children: false,

chunks: false,

chunkModules: false

}) + ‘\n\n’)

if (stats.hasErrors()) {

console.log(chalk.red(‘構(gòu)建失敗\n’))

process.exit(1)

}

console.log(chalk.cyan(‘build完成\n’))

})

將 dll 包合并

const webpack = require(‘webpack’)

module.exports = (config, resolve) => {

return () => {

config.plugin(‘DllPlugin’)

.use(webpack.DllReferencePlugin, [{

context: process.cwd(),

manifest: require(resolve(‘dll/manifest.json’))

}])

}

}

threadLoader

測(cè)試效果變差了 ???,線(xiàn)程數(shù)越小編譯速度越快

config/threadLoader.js

module.exports = (config, resolve) => {

const baseRule = config.module.rule(‘js’).test(/.js|.tsx?$/);

return () => {

const useThreads = true;

if (useThreads) {

const threadLoaderConfig = baseRule

.use(‘thread-loader’)

.loader(‘thread-loader’);

threadLoaderConfig.options({ workers: 3 })

}

}

}

課時(shí) 7:多頁(yè)面配置

注意

棄用?npm run build & npm run dev & npm run dll 改成?box build & box dev & box dll link?npm link 將 box 命令鏈接到全局

本章內(nèi)容

使用 改造為腳手架 多頁(yè)面配置

使用

box build # 不加參數(shù)則會(huì)編譯所有頁(yè)面,并清空 dist

box dev # 默認(rèn)編譯 index 頁(yè)面

參數(shù)

index2 是指定編譯的頁(yè)面。不會(huì)清空 dist

report 開(kāi)啟打包分析

box build index2 --report

box dev index2 --report

改造為腳手架

分成三個(gè)命令,進(jìn)行不同操作

build dev dll

bin/box.js

#!/usr/bin/env node

const chalk = require(‘chalk’)

const program = require(‘commander’)

const packageConfig = require(‘…/package.json’);

const { cleanArgs } = require(‘…/lib’)

const path = require(‘path’)

const name = build,dev,dll

let boxConf = {}

let lock = false

try {

boxConf = require(path.join(process.cwd(), ‘box.config.js’))()

} catch (error) { }

program

.usage(‘ [options]’)

.version(packageConfig.version)

.command(‘build [app-page]’)

.description(構(gòu)建開(kāi)發(fā)環(huán)境)

.option(‘-r, --report’, ‘打包分析報(bào)告’)

.option(‘-d, --dll’, ‘合并差分包’)

.action(async (name, cmd) => {

const options = cleanArgs(cmd)

const args = Object.assign(options, { name }, boxConf)

if (lock) return

lock = true;

if (boxConf.pages) {

Object.keys(boxConf.pages).forEach(page => {

args.name = page;

require(‘…/build/build’)(args)

})

} else {

require(‘…/build/build’)(args)

}

})

program

.usage(‘ [options]’)

.version(packageConfig.version)

.command(‘dev [app-page]’)

.description(構(gòu)建生產(chǎn)環(huán)境)

.option(‘-d, --dll’, ‘合并差分包’)

.action(async (name, cmd) => {

const options = cleanArgs(cmd)

const args = Object.assign(options, { name }, boxConf)

if (lock) return

lock = true;

require(‘…/build/dev’)(args)

})

program

.usage(‘ [options]’)

.version(packageConfig.version)

.command(‘dll [app-page]’)

.description(編譯差分包)

.action(async (name, cmd) => {

const options = cleanArgs(cmd)

const args = Object.assign(options, { name }, boxConf)

if (lock) return

lock = true;

require(‘…/build/dll’)(args)

})

program.parse(process.argv).args && program.parse(process.argv).args[0];

program.commands.forEach(c => c.on(‘–help’, () => console.log()))

if (process.argv[2] && !name.includes(process.argv[2])) {

console.log()

console.log(chalk.red( 沒(méi)有找到 ${process.argv[2]} 命令))

console.log()

program.help()

}

if (!process.argv[2]) {

program.help()

}

多頁(yè)面配置

box.config.js

module.exports = function (config) {

return {

entry: ‘src/main.js’, // 默認(rèn)入口

dist: ‘dist’, // 默認(rèn)打包目錄

publicPath: ‘/’,

port: 8888,

pages: {

index: {

entry: ‘src/main.js’,

template: ‘public/index.html’,

filename: ‘index.html’,

},

index2: {

entry: ‘src/main.js’,

template: ‘public/index2.html’,

filename: ‘index2.html’,

}

},

chainWebpack(config) {

}

}

}

課時(shí) 8:手寫(xiě)一個(gè)webpack插件

如果把 webpack 當(dāng)成一個(gè)垃圾工廠,loader 就是垃圾分類(lèi),將所有垃圾整理好交給 webpack。plugin 就是如何去處理這些垃圾。

webpack 插件寫(xiě)起來(lái)很簡(jiǎn)單,就是你要知道各種各樣的鉤子在什么時(shí)候觸發(fā),然后你的邏輯寫(xiě)在鉤子里面就ok了

apply?函數(shù)是 webpack 在調(diào)用 plugin 的時(shí)候執(zhí)行的,你可以認(rèn)為它是入口 compiler?暴露了和 webpack 整個(gè)生命周期相關(guān)的鉤子 Compilation?暴露了與模塊和依賴(lài)有關(guān)的粒度更小的事件鉤子

本節(jié)概要

實(shí)現(xiàn)一個(gè) CopyPlugin 使用

實(shí)現(xiàn)一個(gè) CopyPlugin

我們今天寫(xiě)一個(gè) copy 的插件,在webpack構(gòu)建完成之后,將目標(biāo)目錄下的文件 copy 到另一個(gè)目錄下

const fs = require(‘fs-extra’)

const globby = require(‘globby’)

class CopyDirWebpackPlugin {

constructor(options) {

this.options = options;

}

apply(compiler) {

const opt = this.options

compiler.plugin(‘done’, (stats) => {

if (process.env.NODE_ENV === ‘production’) {

(async ()=>{

const toFilesPath = await globby([${opt.to}/**, ‘!.git/**’])

toFilesPath.forEach(filePath => fs.removeSync(filePath))

const fromFilesPath = await globby([${opt.from}/**])

fromFilesPath.forEach(fromPath => {

const cachePath = fromPath

fromPath = fromPath.replace(‘dist’, opt.to)

const dirpaths = fromPath.substring(0, fromPath.lastIndexOf(‘/’))

fs.mkdirpSync(dirpaths)

fs.copySync(cachePath, fromPath)

})

console.log( 完成copy ${opt.from} to ${opt.to})

})()

}

});

}

}

module.exports = CopyDirWebpackPlugin

使用

將打包出來(lái)的 dist 目錄下的內(nèi)容 copy 到 dist2 目錄下

const CopyPlugin = require(‘…/webapck-plugin-copy’);

module.exports = ({ config }) => {

return () => {

config.plugin(‘copy-dist’)

.use(CopyPlugin, [{

from: ‘dist’,

to: ‘dist2’

}])

}

}

課時(shí) 9:構(gòu)建 ssr

ssr 就是服務(wù)端渲染,做 ssr 的好處就是為了處理 spa 的不足,比如 seo 優(yōu)化,服務(wù)端緩存等問(wèn)題。

今天主要用 react 的 ssr 來(lái)做一個(gè)簡(jiǎn)單的實(shí)例,讓大家更清晰的入門(mén)

本章概要

創(chuàng)建 box build:ssr 編譯 ssr 編譯 jsx 語(yǔ)法 入口區(qū)分服務(wù)端/客戶(hù)端 服務(wù)端渲染 小結(jié)

創(chuàng)建 box build:ssr

老規(guī)矩,先來(lái)一個(gè)?box build:ssr?命令讓程序可以執(zhí)行

執(zhí)行?box build:ssr?會(huì)調(diào)用?build/ssr?執(zhí)行編譯

program

.usage(‘ [options]’)

.version(packageConfig.version)

.command(‘build:ssr [app-page]’)

.description(服務(wù)端渲染)

.action(async (name, cmd) => {

const options = cleanArgs(cmd);

const args = Object.assign(options, { name }, boxConf);

if (lock) return;

lock = true;

require(‘…/build/ssr’)(args);

});

編譯 ssr

與其他的編譯沒(méi)有什么區(qū)別,值得住的是

target 指定為 umd 模式 globalObject 為 this 入口改為 ssr.jsx

.libraryTarget(‘umd’)

.globalObject(‘this’)

build/ssr.js

module.exports = function(options) {

const path = require(‘path’);

const Config = require(‘webpack-chain’);

const config = new Config();

const webpack = require(‘webpack’);

const rimraf = require(‘rimraf’);

const ora = require(‘ora’);

const chalk = require(‘chalk’);

const PATHS = {

build: path.join(process.cwd(), ‘static’),

ssrDemo: path.join(process.cwd(), ‘src’, ‘ssr.jsx’)

};

require(‘…/config/babelLoader’)({ config, tsx: true })();

require(‘…/config/HtmlWebpackPlugin’)({

config,

options: {

publicPath: ‘/’,

filename: ‘client.ssr.html’

}

})();

config

.entry(‘ssr’)

.add(PATHS.ssrDemo)

.end()

.set(‘mode’, ‘development’) // production

.output.path(PATHS.build)

.filename(‘[name].js’)

.libraryTarget(‘umd’)

.globalObject(‘this’)

.library(‘[name]’)

.end();

rimraf.sync(path.join(process.cwd(), PATHS.build));

const spinner = ora(‘開(kāi)始構(gòu)建項(xiàng)目…’);

spinner.start();

webpack(config.toConfig(), function(err, stats) {

spinner.stop();

if (err) throw err;

process.stdout.write(

stats.toString({

colors: true,

modules: false,

children: false,

chunks: false,

chunkModules: false

}) + ‘\n\n’

);

if (stats.hasErrors()) {

console.log(chalk.red(‘構(gòu)建失敗\n’));

process.exit(1);

}

console.log(chalk.cyan(‘build完成\n’));

});

};

編譯 jsx 語(yǔ)法

因?yàn)槲覀兪怯?react 寫(xiě)的,避免不了會(huì)用到 jsx 語(yǔ)法,所以我們需要在?babel-loader?中使用?@babel/preset-react

npm i @babel/preset-react -D

config/babelLoader.js

if (tsx) {

babelConf.presets.push(‘@babel/preset-react’);

}

入口區(qū)分服務(wù)端/客戶(hù)端

區(qū)分服務(wù)端跟客戶(hù)端分別渲染

const React = require(“react”);

const ReactDOM = require(“react-dom”);

const SSR =

alert(“hello”)}>Hello world;

if (typeof document === “undefined”) {

console.log(‘在服務(wù)端渲染’)

module.exports = SSR;

} else {

console.log(‘在客戶(hù)端渲染’)

const renderMethod = !module.hot ? ReactDOM.render : ReactDOM.hydrate;

renderMethod(SSR, document.getElementById(“app”));

} 自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過(guò),也去過(guò)華為、OPPO等大廠,18年進(jìn)入阿里一直到現(xiàn)在。

深知大多數(shù)前端工程師,想要提升技能,往往是自己摸索成長(zhǎng)或者是報(bào)班學(xué)習(xí),但對(duì)于培訓(xùn)機(jī)構(gòu)動(dòng)則幾千的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效果低效又漫長(zhǎng),而且極易碰到天花板技術(shù)停滯不前!

因此收集整理了一份《2024年Web前端開(kāi)發(fā)全套學(xué)習(xí)資料》,初衷也很簡(jiǎn)單,就是希望能夠幫助到想自學(xué)提升又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。

既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上前端開(kāi)發(fā)知識(shí)點(diǎn),真正體系化!

由于文件比較大,這里只是將部分目錄截圖出來(lái),每個(gè)節(jié)點(diǎn)里面都包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新!

如果你覺(jué)得這些內(nèi)容對(duì)你有幫助,可以?huà)叽a獲取?。。▊渥ⅲ呵岸耍?/p>

最后

今天的文章可謂是積蓄了我這幾年來(lái)的應(yīng)聘和面試經(jīng)歷總結(jié)出來(lái)的經(jīng)驗(yàn),干貨滿(mǎn)滿(mǎn)呀!如果你能夠一直堅(jiān)持看到這兒,那么首先我還是十分佩服你的毅力的。不過(guò)光是看完而不去付出行動(dòng),或者直接進(jìn)入你的收藏夾里吃灰,那么我寫(xiě)這篇文章就沒(méi)多大意義了。所以看完之后,還是多多行動(dòng)起來(lái)吧!

可以非常負(fù)責(zé)地說(shuō),如果你能夠堅(jiān)持把我上面列舉的內(nèi)容都一個(gè)不拉地看完并且全部消化為自己的知識(shí)的話(huà),那么你就至少已經(jīng)達(dá)到了中級(jí)開(kāi)發(fā)工程師以上的水平,進(jìn)入大廠技術(shù)這塊是基本沒(méi)有什么問(wèn)題的了。

');

const chalk = require(‘chalk’);

const PATHS = {

build: path.join(process.cwd(), ‘static’),

ssrDemo: path.join(process.cwd(), ‘src’, ‘ssr.jsx’)

};

require(‘…/config/babelLoader’)({ config, tsx: true })();

require(‘…/config/HtmlWebpackPlugin’)({

config,

options: {

publicPath: ‘/’,

filename: ‘client.ssr.html’

}

})();

config

.entry(‘ssr’)

.add(PATHS.ssrDemo)

.end()

.set(‘mode’, ‘development’) // production

.output.path(PATHS.build)

.filename(‘[name].js’)

.libraryTarget(‘umd’)

.globalObject(‘this’)

.library(‘[name]’)

.end();

rimraf.sync(path.join(process.cwd(), PATHS.build));

const spinner = ora(‘開(kāi)始構(gòu)建項(xiàng)目…’);

spinner.start();

webpack(config.toConfig(), function(err, stats) {

spinner.stop();

if (err) throw err;

process.stdout.write(

stats.toString({

colors: true,

modules: false,

children: false,

chunks: false,

chunkModules: false

}) + ‘\n\n’

);

if (stats.hasErrors()) {

console.log(chalk.red(‘構(gòu)建失敗\n’));

process.exit(1);

}

console.log(chalk.cyan(‘build完成\n’));

});

};

編譯 jsx 語(yǔ)法

因?yàn)槲覀兪怯?react 寫(xiě)的,避免不了會(huì)用到 jsx 語(yǔ)法,所以我們需要在?babel-loader?中使用?@babel/preset-react

npm i @babel/preset-react -D

config/babelLoader.js

if (tsx) {

babelConf.presets.push(‘@babel/preset-react’);

}

入口區(qū)分服務(wù)端/客戶(hù)端

區(qū)分服務(wù)端跟客戶(hù)端分別渲染

const React = require(“react”);

const ReactDOM = require(“react-dom”);

const SSR =

alert(“hello”)}>Hello world;

if (typeof document === “undefined”) {

console.log(‘在服務(wù)端渲染’)

module.exports = SSR;

} else {

console.log(‘在客戶(hù)端渲染’)

const renderMethod = !module.hot ? ReactDOM.render : ReactDOM.hydrate;

renderMethod(SSR, document.getElementById(“app”));

} 自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過(guò),也去過(guò)華為、OPPO等大廠,18年進(jìn)入阿里一直到現(xiàn)在。

深知大多數(shù)前端工程師,想要提升技能,往往是自己摸索成長(zhǎng)或者是報(bào)班學(xué)習(xí),但對(duì)于培訓(xùn)機(jī)構(gòu)動(dòng)則幾千的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效果低效又漫長(zhǎng),而且極易碰到天花板技術(shù)停滯不前!

因此收集整理了一份《2024年Web前端開(kāi)發(fā)全套學(xué)習(xí)資料》,初衷也很簡(jiǎn)單,就是希望能夠幫助到想自學(xué)提升又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。

[外鏈圖片轉(zhuǎn)存中…(img-Qvv7B7zA-1712234940229)]

[外鏈圖片轉(zhuǎn)存中…(img-ms78VScl-1712234940230)]

既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上前端開(kāi)發(fā)知識(shí)點(diǎn),真正體系化!

[外鏈圖片轉(zhuǎn)存中…(img-sqGZb0zj-1712234940230)]

由于文件比較大,這里只是將部分目錄截圖出來(lái),每個(gè)節(jié)點(diǎn)里面都包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新!

如果你覺(jué)得這些內(nèi)容對(duì)你有幫助,可以?huà)叽a獲?。。。▊渥ⅲ呵岸耍?/p>

最后

今天的文章可謂是積蓄了我這幾年來(lái)的應(yīng)聘和面試經(jīng)歷總結(jié)出來(lái)的經(jīng)驗(yàn),干貨滿(mǎn)滿(mǎn)呀!如果你能夠一直堅(jiān)持看到這兒,那么首先我還是十分佩服你的毅力的。不過(guò)光是看完而不去付出行動(dòng),或者直接進(jìn)入你的收藏夾里吃灰,那么我寫(xiě)這篇文章就沒(méi)多大意義了。所以看完之后,還是多多行動(dòng)起來(lái)吧!

可以非常負(fù)責(zé)地說(shuō),如果你能夠堅(jiān)持把我上面列舉的內(nèi)容都一個(gè)不拉地看完并且全部消化為自己的知識(shí)的話(huà),那么你就至少已經(jīng)達(dá)到了中級(jí)開(kāi)發(fā)工程師以上的水平,進(jìn)入大廠技術(shù)這塊是基本沒(méi)有什么問(wèn)題的了。

資料領(lǐng)取方式:戳這里前往免費(fèi)領(lǐng)取

柚子快報(bào)激活碼778899分享:webpack learn

http://yzkb.51969.com/

好文鏈接

評(píng)論可見(jiàn),查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19719232.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問(wèn)

文章目錄