Q1: Что такое загрузка по требованию?
Поскольку разработка одностраничных приложений становится все больше и больше, разделение js является первоочередной задачей, и разделенные js могут загружаться выборочно в соответствии с нашими потребностями.
Итак, первый вопрос, как разобрать js?
Q2: Как его удалить?
1. Как он выглядел до того, как был разделен?
Приходите на демо, посмотрим, как это выглядит до разделения: a.js:
import b from './b.js';
console.log("this is a.js")
const btn = document.querySelector("#btn");
btn.onclick = ()=>{
b();
}
b.js:
export default ()=>{
console.log("this is b");
}
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="btn">btn</div>
<script src="./dist/main.js"></script>
</body>
</html>
webpack.config.js
module.exports = {
entry:'./a.js',
output:{
filename:'[name].js'
}
}
- a.js ссылается на b.js
- Пакет webpack упаковывает b и a вместе и выводит файл main.js по умолчанию.
- html ссылается на упакованный main.js Результат выглядит следующим образом:
2. Начинайте!
шаг 1: изменить webpack.config.js
module.exports = {
entry:'./a.js',
output:{
filename:'[name].js',
chunkFilename:'[name].js'// 设置按需加载后的chunk名字
}
}
Вот предложение, chunkFilename просто, функция chunkFilename — это элемент конфигурации, используемый для именования разделенных фрагментов. ок, запусти вебпак
По-прежнему упакован только один main.js, без изменений... Не волнуйтесь, это потому, что еще нужно выполнить настройки.
Шаг 2: Изменить a.js
// import b from './b.js';
console.log("this is a.js")
const btn = document.querySelector("#btn");
btn.onclick = ()=>{
import('./b').then(function(module){
const b = module.default;
b();
})
}
- Используйте синтаксис импорта по требованию es6
- Выполнить возвращенный результат после обещания В этот момент снова запустите webpack:
Выходной файл становится двое, один main.js и один 1.js Это 1.js сумасшедший ...
Посмотрите на исходный код, можно увидеть, на самом деле, это наше b.js
В заключение :
- Настройка вывода в webpack не решает, разбивать код или нет
- Определяющим фактором разделения кода является синтаксис импорта.
- Когда webpack сканирует код с синтаксисом импорта, он решает выполнить разделенный код.
Шаг 3: Как его использовать?
Что ж, я успешно сообщил об ошибке... у меня болит мозг Ошибка анализа:
- Файл загружается по запросу, чтобы найти /1.js
- Но результат нашей упаковки находится в каталоге dist, в корневом каталоге его естественно найти невозможно
Шаг 4. Настройте базовый путь общего пути.
Эта конфигурация помогает указать базовый путь для всех ресурсов в проекте. Это называется公共路径(publicPath)
.Изменить webpack.config.js
module.exports = {
entry:'./a.js',
output:{
filename:'[name].js',
chunkFilename:'[name].js',// 设置按需加载后的chunk名字
publicPath:'dist/' // 设置基础路径
}
}
шаг 5: проверьте результат
- до щелчка
- Упоминается только main.js
- После нажатия
- 1.js загружен
- И выполнить код js в 1.js
- Вывод консоли это b.js
- хорошо, проверка прошла успешно
Шаг 6: Заполните яму
Предыдущий 1.js не читается, и трудно выяснить, есть ли проблемы.Webpack предоставляет способ определить имена фрагментов по запросу, изменить a.js:
// import b from './b.js';
console.log("this is a.js")
const btn = document.querySelector("#btn");
btn.onclick = ()=>{
import(/* webpackChunkName: "b" */ './b').then(function(module){
const b = module.default;
b();
})
}
Перед динамически вводимой грамматикой добавляется комментарий, который является способом указания фрагмента. Результат:
Выводится B.js, и тест возвращается один раз:
- Имена чанков не влияют на загрузку по требованию.
- Изменение имени фрагмента по запросу просто для облегчения чтения файла.
Q3: Можно ли выполнить горячее обновление после загрузки по требованию?
1, сначала запустите интеграцию webpack-dev-server
Сначала установите webpack-dev-server и настройте сценарии npm.
{
"devDependencies": {
"webpack-dev-server": "^3.1.9"
},
"scripts": {
"start:dev": "webpack-dev-server"
},
"dependencies": {
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2"
}
}
Изменить webpack.config.js
var path = require('path');
module.exports = {
entry:'./a.js',
mode:'development',
output:{
filename:'[name].js',
chunkFilename:'[name].js',// 设置按需加载后的chunk名字
publicPath:'dist/'
},
devServer: {
contentBase: './',
compress: true,
port: 9000
}
}
- На этот раз он больше не выполняется через команду webpack.
- Вместо этого он выполняется через командную строку npm run start:dev.
- webpack-dev-server прочитает конфигурацию devServer в webpack.config.js
- хорошо, devServer был интегрирован
2, подбегаю и вижу
Изменить webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry:'./a.js',
mode:'development',
output:{
filename:'[name].js',
chunkFilename:'[name].js',// 设置按需加载后的chunk名字
publicPath:'dist/'
},
devServer: {
contentBase: './',
compress: true,
port: 9000,
hot: true, // 开启热更新
},
plugins: [ // 开始热更新
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
}
Всего 3 фразы:
- горячий оператор в devServer
- Два встроенных плагина webpack в plugins После того, как эти два плагина включены, он все равно не работает, и файл входа нужно изменить.
// import b from './b.js';
console.log("this is a.js")
const btn = document.querySelector("#btn");
btn.onclick = ()=>{
import(/* webpackChunkName: "b" */ './b').then(function(module){
const b = module.default;
b();
})
}
if (module.hot) {// 开启热替换
module.hot.accept()
}
хорошо, это так просто, горячее обновление + загрузка по требованию будут работать вместе.
Q4: интеграция с реактивным маршрутизатором, загрузка по требованию
В бизнесе, в дополнение к загрузке по запросу при нажатии, в большинстве сценариев используется загрузка по запросу при переключении маршрутизации.
Шаг 1: Добавьте загрузчик babel
Изменить webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry:'./a.js',
mode:'development',
output:{
filename:'[name].js',
chunkFilename:'[name].js',// 设置按需加载后的chunk名字
publicPath:'dist/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
}
}
]
},
devServer: {
contentBase: './',
compress: true,
port: 9000,
hot: true,
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
}
Новое добавление выше - добавить загрузчик babel
шаг 2: добавьте .babelrc
{
"presets": ["@babel/preset-react","@babel/preset-env"]
}
шаг 3: написать jsx
Изменить a.js
import React,{Component} from 'react';
import ReactDom from 'react-dom';
import B from './b.js';
export default class A extends Component{
render(){
return <div>
this is A
<B />
</div>
}
}
ReactDom.render(<A/>,document.querySelector("#btn"))
if (module.hot) {
module.hot.accept()
}
Изменить b.js
import React,{Component} from 'react';
export default class B extends Component{
render(){
return <div>this is B</div>
}
}
есть тест:
- реакция запущена
- Горячее обновление все еще работает
Шаг 4: Интеграцияreact-loadable
Загрузка React on-demand развивалась несколькими способами, последним из которых является использование компонента react-loadable.Официальный также рекомендует использовать эту библиотеку для достижения, в настоящее время эта библиотека имеет 1w+star
Изменить a.js
import React,{Component} from 'react';
import { BrowserRouter as Router, Route, Switch,Link } from 'react-router-dom';
import ReactDom from 'react-dom';
import Loadable from 'react-loadable';
const Loading = () => <div>Loading...</div>;
const B = Loadable({
loader: () => import('./b.js'),
loading: Loading,
})
const C = Loadable({
loader: () => import('./C.js'),
loading: Loading,
})
export default class A extends Component{
render(){
return <div>
<Router>
<div>
<Route path="/B" component={B}/>
<Route path="/C" component={C}/>
<Link to="/B">to B</Link><br/>
<Link to="/C">to C</Link>
</div>
</Router>
</div>
}
}
ReactDom.render(<A/>,document.querySelector("#btn"))
if (module.hot) {
module.hot.accept()
}
- Синтаксис импорта, используемый в loadable, представляет собой функцию динамической загрузки, которую ECMA будет поддерживать в будущем.
- Loadable очень прост, вам нужно только обернуть компоненты, которые необходимо загрузить, в соответствии с указанным синтаксисом.
Нажмите, чтобы перейти к C
Вы можете видеть, что 1.js загружен, что означает, что асинхронная загрузка завершена успешно. Но теперь есть проблема: обновляйтесь по пути /C, будет ситуация, когда маршрут не может попасть
Шаг 5: запустите экспресс для проверки
var express = require('express')
var app = express()
app.use(express.static('dist'))
app.get('*', function (req, res) {
res.send(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="btn">btn</div>
<script src="./main.js"></script>
</body>
</html>`)
})
app.listen(5000);
Создайте простое экспресс-приложение:
- проверено
- Также выполняет загрузку по требованию
Шаг 6: Вложенные маршруты загружаются по запросу
Очень распространенной функцией маршрутизации является вложенность маршрутов, поэтому наша загрузка по требованию должна поддерживать вложенную маршрутизацию, чтобы быть разумной. Изменить a.js
import React,{Component} from 'react';
import { BrowserRouter as Router, Route, Switch,Link } from 'react-router-dom';
import ReactDom from 'react-dom';
import Loadable from 'react-loadable';
const Loading = (props) => {
return <div>Loading...</div>
};
const B = Loadable({
loader: () => import('./b.js'),
loading: Loading,
})
const C = Loadable({
loader: () => import('./c.js'),
loading: Loading,
})
export default class A extends Component{
render(){
return <div>
<Router>
<div>
<Route path="/B" component={B}/>
<Route path="/C" component={C}/>
<Link to="/B">to B</Link><br/>
<Link to="/C">to C</Link>
</div>
</Router>
</div>
}
}
ReactDom.render(<A/>,document.querySelector("#btn"))
if (module.hot) {
module.hot.accept()
}
Изменить c.js
import React,{Component} from 'react';
import { Route,Link} from 'react-router-dom';
import Loadable from 'react-loadable';
const Loading = (props) => {
return <div>Loadingc...</div>
};
const D = Loadable({
loader: () => import('./d.js'),
loading: Loading,
})
export default class C extends Component{
render(){
return <div>
this is C
<Route path="/C/D" component={D}/>
<Link to="/C/D">to D</Link>
</div>
}
}
- Входной файл вводит две динамические маршруты B и C
- Вложенная маршрутизация C.JS / C / D
- Компонент D по требованию используется в маршруте /C/D.
Шаг 7: Проверка вложенных маршрутов
Вход в порядке
Нажмите, чтобы перейти к динамической загрузке C без проблем
Нажмите, чтобы прыгнуть D
Вы можете видеть, что возникло исключение, когда ресурс ./d.js был динамически импортирован, а путь /C был добавлен необъяснимым образом.
шаг 8: чертpublicPath
Некоторое время я гадал здесь, и я проверил много контента, наконец, я понял, что должна быть проблема с настройкой publicPath, перепроверил настройки и изменил webpack.config.js.
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry:'./a.js',
mode:'development',
output:{
path:path.resolve(__dirname, 'dist'),
filename:'[name].js',
chunkFilename:'[name].js',// 设置按需加载后的chunk名字
publicPath:'/dist/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
}
}
]
},
devServer: {
contentBase: './',
compress: true,
port: 9000,
hot: true,
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
}
Единственное изменение здесь заключается в том, что publicPath изменен с исходного dist/ на /dist/. Пока добавлен предыдущий путь, относительный адрес не будет найден.
Q5: Как это сделать в реальном проекте?
Фронт вроде решает проблему, но в реальной сцене наши требования точно будут выше! Во-первых, инкапсулировать удобный компонент загрузки по требованию.
шаг 1: инкапсулируйте компонент LazyLoad
Идеал прекрасен, реальность - это основа
const LazyLoad = (path)=>{
return Loadable({
loader: () => import(path),
loading: Loading,
})
}
const B = LazyLoad('./b.js')
Затем я получил ошибку
Это связано с тем, что при компиляции веб-пакета предварительная доставка импорта == не поддерживает динамические пути ==
Шаг 2: ужасный импорт, узнайте об этом
Импорт не поддерживает динамические пути, потому что веб-пакет должен сначала сканировать файл js, выяснить, какие части загружаются по запросу, и упаковать его по требованию, но не заботится о внутреннем контексте выполнения js, то есть, когда веб-пакет сканирует, переменные в js не вычисляют результаты, поэтому импорт не поддерживает динамические пути.
Шаг 3: инкапсулировать не импортируемые части
Поскольку импорт невозможен, он может инкапсулировать только не импортируемую часть.
const LazyLoad = loader => Loadable({
loader,
loading:Loading,
})
Отделите часть загрузчика в качестве параметра, следующее конкретное использование
const B = LazyLoad(()=>import('./b.js'));
const C = LazyLoad(()=>import('./c.js'));
Ниже весь код
import React,{Component} from 'react';
import { BrowserRouter as Router, Route, Switch,Link } from 'react-router-dom';
import ReactDom from 'react-dom';
import Loadable from 'react-loadable';
const Loading = (props) => {
return <div>Loading...</div>
};
const LazyLoad = loader => Loadable({
loader,
loading:Loading,
})
const B = LazyLoad(()=>import('./b.js'));
const C = LazyLoad(()=>import('./c.js'));
export default class A extends Component{
render(){
return <div>
<Router>
<div>
<Route path="/B" component={B}/>
<Route path="/C" component={C}/>
<Link to="/B">to B</Link><br/>
<Link to="/C">to C</Link>
</div>
</Router>
</div>
}
}
ReactDom.render(<A/>,document.querySelector("#btn"))
if (module.hot) {
module.hot.accept()
}
Вышеупомянутый метод упаковки не идеален, в документации к веб-пакету говорится, что он поддерживает:
==импорт(./dynamic/\${path}
) способ ==
Поскольку не все переменные поддерживаются, это зависит от конкретной бизнес-формы.Если все части по запросу находятся в определенном каталоге, эта операция может быть более удобной.
В соответствии с текущим методом это кажется громоздким, но поддержка пути может быть достигнута путем настройки псевдонима alias webpack.
Q6: Загружать +конфигурацию маршрутизатора по требованию
В дополнение к компонентному методу, реагирующий маршрутизатор также можно настроить с помощью метода конфигурации, который удобен для унифицированного обслуживания уровня контроллера.
шаг 1: инкапсулировать отложенную загрузку
Создайте файл LazyLoad.js
import React from 'react';
import Loadable from 'react-loadable';
const Loading = (props) => {
return <div>Loading...</div>
};
export default loader => Loadable({
loader,
loading:Loading,
})
Сначала отдельно инкапсулируйте компонент Lazyload.
Шаг 2: настроить маршруты
Создать route.js
import LazyLoad from './LazyLoad';
export default [
{
path: "/B",
component: LazyLoad(()=>import('./b.js'))
},
{
path: "/C",
component: LazyLoad(()=>import('./c.js')),
routes: [
{
path: "/C/D",
component: LazyLoad(()=>import('./d.js'))
},
{
path: "/C/E",
component: LazyLoad(()=>import('./e.js'))
}
]
}
];
Настройте файл маршрутов для динамического импорта маршрутов.
Шаг 3: метод пакетного инструментаRouteWithSubRoutes
Создать utils.js
import React from 'react';
import {Route} from 'react-router-dom';
export const RouteWithSubRoutes = route => (
<Route
path={route.path}
render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes} />
)}
/>
);
==Этот шаг особенно важен, особенно важен, особенно важен==
Роль этого метода инструмента заключается в отображении компонента
Шаг 4: Измените запись маршрутизации первого уровня.
import React,{Component} from 'react';
import { BrowserRouter as Router, Route, Switch,Link } from 'react-router-dom';
import ReactDom from 'react-dom';
import {RouteWithSubRoutes} from './utils';
import routes from './routes';
export default class A extends Component{
render(){
return <div>
<Router>
<div>
<Link to="/B">to B</Link><br/>
<Link to="/C">to C</Link>
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
</div>
</Router>
</div>
}
}
ReactDom.render(<A/>,document.querySelector("#btn"))
if (module.hot) {
module.hot.accept()
}
- Введение метода инструмента RouteWithSubRoutes
- Импортируйте файл конфигурации маршрутизации маршрутов
- Отрисовка обхода маршрутов в упакованном файле
==Примечание: здесь обрабатывается только первый уровень маршрутизации== ==Примечание: здесь обрабатывается только первый уровень маршрутизации== ==Примечание: здесь обрабатывается только первый уровень маршрутизации==
Шаг 5: Измените запись вторичной маршрутизации
После настройки маршрутизации вложенные подмаршруты должны быть написаны в функциональном стиле.
import React,{Component} from 'react';
import {RouteWithSubRoutes} from './utils';
import { Link} from 'react-router-dom';
export default ({ routes }) => (
<div>
this is C
<Link to="/C/D">to D</Link>
<Link to="/C/E">to E</Link>
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
</div>
);
- Введение метода инструмента RouteWithSubRoutes
- Открытая функция принимает параметры route
- Маршруты — это конфигурация внутреннего слоя в конфиге, то есть конфигурация маршрутизации второго уровня
- Вторичная конфигурация маршрутизации, продолжайте рендеринг через RouteWithSubRoutes
==注意:config嵌套路由,需要逐层,一层一层的通过RouteWithSubRoutes来渲染。 == ==新人很容易忽视这一点! == ==新人很容易忽视这一点! == ==新人很容易忽视这一点! ==
Q7: Используйте роутер по своему усмотрению?
Маршрут настраивается в методе конфига, но на самом деле он может быть и здесь смешанным, то есть смешаны метод конфига + метод компонента. Измените запись вторичного маршрута:
import React from 'react';
import { Link,Route} from 'react-router-dom';
//import {RouteWithSubRoutes} from './utils';
import LazyLoad from './LazyLoad';
const D = LazyLoad(() => import('./d.js'))
const E = LazyLoad(() => import('./e.js'))
export default ({ routes }) => (
<div>
this is C
<Route path="/C/D" component={D}/>
<Route path="/C/E" component={E}/>
<Link to="/C/D">to D</Link>
<Link to="/C/E">to E</Link>
{/* {routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)} */}
</div>
);
На самом деле, здесь просто делай, что хочешь
Для маршрутизации лучше вести ее равномерно.Конечно, вы также можете выбрать нужный метод в соответствии с бизнесом! .
Загрузка веб-пакета по требованию, которая вызывает головную боль, подошла к концу.