иллюстрировать
- Проект в основном обсуждает и записывает основной процесс построения системы веб-инженерии. (Без cli, предоставляемого фреймворком, создайте довольно полное веб-приложение с основ)
- Современные веб-проекты в основном представляют собой три фреймворка React, Vue и Angular. Выберите React для проекта.
- Почему стоит выбрать Реакт? Причина в том, что в работе используется Vue, для этого проекта не очень важно, что выбрать, главное,Обсудить процесс создания хорошего проекта.
- Угол обсуждения:
- Управление версиями (git)
- управление пакетами npm
- webpack (создать среду разработки и упаковать онлайн-ресурсы)
- Качество спецификации кода (eslint, stylint, prettier)
- Модульное тестирование (компоненты пользовательского интерфейса, приватная служебная функция «шутка»)
- Организация каталога проектов
- Разделение интерфейса и сервера (MOCK, json-сервер)
- Взаимодействие между клиентом и сервером (axios)
- Спецификация написания стиля компонента
- разделение компонентов
Давайте начнем
1. Управление версиями (git)
$ 如何安装 ?
# https://git-scm.com/downloads
$ 如何使用 ?
# https://git-scm.com/doc || git --help
2. Create a local .gitignore
Иногда некоторые файлы не хотят
Git
РегистрироватьсяGitHub
..gitignore
Файл конфигурации может сказатьGit
Какие файлы игнорировать.
$ touch .gitignore # starter/.gitignore
# dependencies
/node_modules
# testing
/coverage
# production
/build
dist
# misc
.DS_Store
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.vscode
3. node,npmа такжеyarn
$ node 是什么 ?
# http://nodejs.cn/
$ 如何安装 ?
# http://nodejs.cn/download/
$ npm 是什么 ?
# https://docs.npmjs.com/about-npm/
$ npm 如何使用 ?
# 安装 Node.js 时附带安装了 npm || npm -v
$ 创建包管理配置文件 package.json
# https://docs.npmjs.com/creating-a-package-json-file
$ package.json 文件中的要求 ?
# https://docs.npmjs.com/files/package.json.html
$ package-lock.json 是什么 ?
# https://docs.npmjs.com/files/package-lock.json.html
$ yarn 是什么?
# https://yarn.bootcss.com/
$ yarn 如何安装 ?
# https://yarn.bootcss.com/docs/install/#mac-stable
$ yarn 如何使用 ?
# https://yarn.bootcss.com/docs/
$ 初始化项目
# mkdir starter && npm init
初始工程目录
а такжеpackage.json
информация ✅
Каталог проекта
└── starter
├── README.md
└── package.json
package.json
{
"name": "starter",
"version": "1.0.0",
"description": "List of engineering builds for web applications",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cllemon/starter.git"
},
"keywords": [
"javascript",
"typescript",
"react"
],
"author": "cllemon",
"license": "MIT",
"bugs": {
"url": "https://github.com/cllemon/starter/issues"
},
"homepage": "https://github.com/cllemon/starter#readme"
}
Примечание. Все установки пакетов в следующих описаниях используют
yarn
Заказ
4. editorconfig
EditorConfig
может помочь разработчикам в разных редакторах иIDE
Определите и поддерживайте единый стиль кода между ними.
EditorConfig is awesome: editorconfig.org
$ touch .editoorconfig
# starter/.editoorconfig
root = true # 表明是最顶层的配置文件,发现设为 true 时,才会停止查找.editorconfig 文件。
[*]
charset = utf-8
indent_style = space # tab 为 hard-tabs,space 为 soft-tabs。
indent_size = 2 # 规定每级缩进的列数和 soft-tabs 的宽度(空格数)。如果设定为 tab,则会使用 tab_width 的值。
end_of_line = lf # 定义换行符,支持 lf(UNIX/Linux采用换行符 LF 表示下一行)、cr(MAC OS系统)则采用回车符 CR 表示下一行) 和 crlf。
insert_final_newline = true # 设为 true 表明使文件以一个空白行结尾,false 反之
trim_trailing_whitespace = true # 设为 true 表示会除去换行行首的任意空白字符,false 反之。
[*.md] # 校验 markdown 文档
insert_final_newline = false
trim_trailing_whitespace = false
5. browserslist
-
browserslist
что это такое?Используется для обмена целевыми браузерами и
Node.js
конфигурация версии. НапримерAutoprefixer
,Stylelint
а такжеbabel-preset-env
. -
browserslist
Метод конфигурациикогда вы добавляете следующее в
package.json
или .browserslistrc, все инструменты автоматически найдут целевой браузер:# package.json { "browserslist": { "production": [ // 生产环境配置 ">0.2%", // 支持市场份额大于 1% 的浏览器。 "not dead", // not(逻辑非)对 dead 取反,而浏览器被认为是 dead 条件是:最新的两个版本中发现其市场份额已经低于 0.5% 并且 24 个月内没有官方支持和更新。 "not op_mini all" // OperaMini or op_mini for Opera Mini. ], "development": [ // 开发环境配置 "last 1 chrome version", // 浏览器版本查询范围, chrome 最近的一个版本 "last 1 firefox version", "last 1 safari version" ] } } 或🔥 # .browserslistrc $ touch .browserslist [production] > .2% not dead not op_mini all [development] last 1 chrome version last 1 firefox version last 1 safari version
6. импортировать веб-пакет
По сути,
webpack
современныйJavaScript
Инструмент упаковки статических модулей для приложений. когдаwebpack
При обработке приложение внутренне строит граф зависимостей (dependency graph
), этот граф зависимостей отображает каждый модуль, требуемый проектом, и генерирует один или несколькоbundle
.
-
Установите и создайте основные файлы
$ mkdir src # 创建存放核心代码文件夹 $ cd src && touch index.js # 创建入口文件 $ yarn add -D webpack # 安装最新版本 webpack^4.41.2 $ yarn add -D webpack-cli # 安装 webpack v4+ 版本,所需的 webpack-cli^3.3.9 $ cd .. && touch webpack.config.js # 根目录,创建 webpack 基本配置文件
-
Каталог проекта
└── starter + ├── node_modules + ├── src + │ └── index.js + ├── webpack.config.js ├── package.json └── README.md
7. Импорт
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Установите и создайте основные файлы
$ yarn add react # 安装 react^16.10.2 $ yarn add react-dom # 安装 react-dom^16.10.2 $ mkdir public # 新建公共资源文件夹 $ cd public && touch index.html # 新建 html 文件 $ copy favicon.ico # 添加 网页图标 文件 $ cd .. # 回到根目录
-
Напишите файл index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="./favicon.ico" /> <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="This is a react application built from scratch with JavaScript, away from the cli tool." /> <title>Starter</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body> </html>
-
Напишите файл index.js
import React from 'react'; import ReactDom from 'react-dom'; const App = () => <h1>Hello, world!</h1> ReactDom.render(<App />, document.getElementById('root'));
Примечание. Поскольку браузер не поддерживает последний синтаксис JavaScript и синтаксический анализ react jsx, нам нужен компилятор, который нам поможет.
8. Представляем Вавилон
Babel — это цепочка инструментов, в основном используемая для преобразования кода ECMAScript 2015+ в обратно совместимые версии кода JavaScript в старых браузерах или средах.
-
Вавилонская инсталляция
$ yarn add -D @babel/core # Babel 编译器核心模块 $ yarn add -D @babel/preset-env # 是一个智能预设,它使您可以使用最新的JavaScript,而无需微观管理目标环境所需的语法转换 $ yarn add -D @babel/preset-react # react 智能预设, 包含了解析 jsx 等插件 $ yarn add -D babel-loader # Babel loader for webpack 该软件包允许使用 Babel 和 webpack 来转译 JavaScript 文件。 $ touch .babelrc # 新建 babel 配置文件
-
вавилонская конфигурация
// .babelrc { "presets": ["@babel/preset-env", "@babel/preset-react"], }
-
Написать конфигурацию веб-пакета
// starter/webpack.config.js const path = require('path'); module.exports = function() { const baseConfig = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, ] } }; return baseConfig; };
-
Измените package.json, чтобы добавить команду webpack для быстрого запуска.
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "build": "webpack --color --progress" } }
-
Измените index.html, чтобы импортировать файл bundle.js после упаковки.
... <div id="root"></div> + <script src="../dist/bundle.js"></script> ...
-
запустить проект
$ yarn build # 打包文件 Hash: 6e4adf36d533e9d646c0 Version: webpack 4.41.2 Time: 693ms Built at: 2019-10-19 11:41:22 Asset Size Chunks Chunk Names bundle.js 1.09 MiB main [emitted] main Entrypoint main = bundle.js [./src/index.js] 233 bytes {main} [built] # 浏览器 打开 index.html 查看效果
-
Каталог проекта
└── starter + ├── dist + │ └── bundle.js ├── node_modules + ├── public + │ ├── favicon.ico + │ └── index.html ├── src │ └── index.js ├── webpack.config.js ├── package.json ├── README.md + └── yarn.lock
9. Настройка среды разработки — с помощью webpack-dev-server
webpack-dev-server предоставляет вам простой веб-сервер с возможностью перезагрузки в реальном времени.
-
Установить
$ yarn add -D webpack-dev-server # 用于快速开发应用程序
-
Добавьте соответствующую конфигурацию
// starter/webpack.config.js const path = require('path'); module.exports = function() { const baseConfig = { devtool: 'inline-source-map', // 控制是否生成,以及如何生成 source map entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, ] }, + devServer: { + contentBase: path.resolve(__dirname, 'public'), // 告诉服务器从哪个目录中提供内容 + historyApiFallback: true, // 启用当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。 + compress: true, // 一切服务都启用 gzip 压缩 + open: true, // 告诉 dev-server 在 server 启动后打开浏览器 + port: 3000, // 指定要监听请求的端口号 + stats: 'errors-only', // 精确控制要显示的 bundle 信息 (在 bundle 中只显示错误) + } }; return baseConfig; };
-
Измените package.json, чтобы добавить команду webpack для быстрого запуска.
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --color --progress", + "server": "webpack-dev-server --color --progress" } }
--color
: включить/выключить вывод цвета консоли;--progress
: вывод хода выполнения на консоль. -
Измените путь к основному файлу index.html bundle.js.
... <div id="root"></div> - <script src="../dist/bundle.js"></script> + <script src="bundle.js"></script> ...
-
запустить проект
$ yarn server # 结果: $ webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully.
Открыть
http://localhost:3000/
покажетHello, world!
; Исправлятьsrc/index.js
Браузер будет обновлен, чтобы обновлять изменения в режиме реального времени. Попытайся! -
Есть проблемы или точки улучшения, которые нужно улучшить
- Каждое изменение требует обновления всего браузера, что явно не соответствует современному опыту инженерных разработок!
- Недифференцированная среда (
webpack.config.js
Некоторые конфигурации мы хотим иметь только в среде разработки, а в рабочей среде должны быть определенные конфигурации)
С этими вопросами вперед! 👍
10. Настройка среды разработки — переменные среды
-
Установить
$ yarn add -D cross-env # Cross platform setting of environment scripts
-
Изменить команду webpack package.json
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "webpack --color --progress", + "build": "cross-env NODE_ENV=production webpack --color --progress", - "server": "webpack-dev-server --color --progress" + "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress" } }
-
Добавьте соответствующую конфигурацию в webpack.config.js.
// starter/webpack.config.js const path = require('path'); + const IS_PROD = process.env.NODE_ENV === 'production'; module.exports = function() { const baseConfig = { + mode: IS_PROD ? 'production' : 'development', - devtool: 'inline-source-map', // 控制是否生成,以及如何生成 source map + devtool: IS_PROD ? false : 'inline-source-map', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, ] }, - devServer: { - contentBase: path.resolve(__dirname, 'public'), // 告诉服务器从哪个目录中提供内容 - historyApiFallback: true, // 启用当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。 - compress: true, // 一切服务都启用 gzip 压缩 - open: true, // 告诉 dev-server 在 server 启动后打开浏览器 - port: 3000, // 指定要监听请求的端口号 - stats: 'errors-only', // 精确控制要显示的 bundle 信息 (在 bundle 中只显示错误) - } }; + if (!IS_PROD) { + baseConfig.devServer = { + contentBase: path.resolve(__dirname, 'public'), // 告诉服务器从哪个目录中提供内容 + historyApiFallback: true, // 启用当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。 + compress: true, // 一切服务都启用 gzip 压缩 + open: true, // 告诉 dev-server 在 server 启动后打开浏览器 + port: 3000, // 指定要监听请求的端口号 + stats: 'errors-only', // 精确控制要显示的 bundle 信息 (在 bundle 中只显示错误) + }; + } return baseConfig; };
11. Настройка среды разработки - Горячая замена модуля
Горячая замена модуля (или HMR) — одна из самых полезных функций, предоставляемых webpack. Он позволяет обновлять все типы модулей во время выполнения без полного обновления.
-
Добавьте соответствующую конфигурацию в webpack.config.js.
// starter/webpack.config.js const path = require('path'); + const webpack = require('webpack'); const IS_PROD = process.env.NODE_ENV === 'production'; module.exports = function() { const baseConfig = { mode: IS_PROD ? 'production' : 'development', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, ] }, + plugins: [] }; if (!IS_PROD) { baseConfig.devServer = { contentBase: path.resolve(__dirname, 'public'), // 告诉服务器从哪个目录中提供内容 historyApiFallback: true, // 启用当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。 compress: true, // 一切服务都启用 gzip 压缩 open: true, // 告诉 dev-server 在 server 启动后打开浏览器 port: 3000, // 指定要监听请求的端口号 stats: 'errors-only', // 精确控制要显示的 bundle 信息 (在 bundle 中只显示错误) + hot: true // 启用 webpack 的 模块热替换 功能 }; + baseConfig.plugins.concat([ + new webpack.HotModuleReplacementPlugin() // 热替换模块插件 + ]); } return baseConfig; };
-
Измените файл src/index.js.
- import React from 'react'; + import React, { useState } from 'react'; import ReactDom from 'react-dom'; - const App = () => <h1>Hello, world!</h1>; + const App = () => { + const [title, setTitle] = useState('hello, world!'); + const reversedTitle = () => + setTitle( + title + .split('') + .reverse() + .join('') + ); + return ( + <div> + <h1>{ title }</h1> + <button type='button' onClick={reversedTitle}> + reversed title + </button> + </div> + ); + }; + if (module.hot) { + module.hot.accept(); + } ReactDom.render(<App />, document.getElementById('root'));
-
запустить проект
$ yarn server # 结果: $ cross-env NODE_ENV=development webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully. # 浏览器 console [HMR] Waiting for update signal from WDS... log.js:24 [WDS] Hot Module Replacement enabled. client:48 [WDS] Live Reloading enabled. client:52
Открыть
http://localhost:3000/
Исправлятьsrc/index.js
Реализована модификация необновляемого обновления браузера. Попытайся! -
Есть проблемы или точки улучшения, которые нужно улучшить
- Каждый раз, когда содержимое модифицируется, обновления обновления не происходит, но в то же время очищается значение внутреннего состояния компонента, что явно недопустимо.
Имея в виду этот вопрос, вперед! ✈️
12. Создайте среду разработки - модуль горячей замены - импортируйте react-hot-loader
Настраивайте компоненты React в режиме реального времени.
-
иллюстрировать
- Поскольку проект выбирает фреймворк реакции, вводится react-hot-loader.
- Если вы переключаетесь на другие фреймворки, также есть соответствующие интеграции плагинов, такие как: vue-hot-reload-api, интегрированный в vue-loader.
- Конечно, вы также можете сделать это самостоятельно.
-
Установить
$ yarn add react-hot-loader $ yarn add @hot-loader/react-dom # 替换了相同版本的 react-dom 软件包,但附加了一些补丁以支持热重装。
-
Буду
"react-hot-loader/babel"
добавить в свой.babelrc
середина{ "presets": ["@babel/preset-env", "@babel/preset-react"], + "plugins": ["react-hot-loader/babel"] }
-
сброс настроек
react-dom
совместимыйhooks
... moduele.exports = function () { ... + resolve: { + alias: { + 'react-dom': '@hot-loader/react-dom' // react-hot-loader 兼容 hook 写法 + } + }, ... } ...
-
Измените основной файл src/index.js, чтобы пометить корневой компонент как
hot-exported
+ import { hot } from 'react-hot-loader'; import React, { useState } from 'react'; import ReactDom from 'react-dom'; - const App = () => { + const App = hot(module)(() => { const [title, setTitle] = useState('hello, world!'); const reversedTitle = () => setTitle( title .split('') .reverse() .join('') ); return ( <div> <h1>{title}</h1> <button type='button' onClick={reversedTitle}> reversed title! </button> </div> ); - }; + }); - if (module.hot) { - module.hot.accept(); - } ReactDom.render(<App />, document.getElementById('root'));
-
запустить проект
$ yarn server # 结果: $ cross-env NODE_ENV=development webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully. ℹ 「wdm」: Compiling... ℹ 「wdm」: Compiled successfully. # 浏览器 console [HMR] Waiting for update signal from WDS... log.js:24 [WDS] App hot update... reloadApp.js:19 [HMR] Checking for updates on the server... log.js:24 [HMR] Updated modules: log.js:24 [HMR] - ./src/index.js log.js:24 [HMR] App is up to date. log.js:24
Открыть
http://localhost:3000/
, нажмитеreversed title
затем изменитьsrc/index.js
Реализована модификация обновления, сохраняющая состояние компонента без обновления браузера. Попытайся! -
сценический эпилог
- На данный момент создана простая среда разработки. 🐃
- Впереди еще много работы, продолжайте в том же духе! 🍺
13. Представьте CSS иSassобработка файла стилей
Стили — важная часть интерфейсных компонентов, а Sass делает язык CSS более мощным и элегантным, помогает поддерживать хорошую структуру больших таблиц стилей.
Примечание. Этот проект представляет sass, конечно, вы также не можете импортировать или импортировать другие, такие как: меньше, стилус.
-
Установить
$ yarn add -D node-sass # Node-sass是一个库,提供了 Node.js 与 LibSass(流行的样式表预处理器Sass的C版本)的绑定。 它使您能够以惊人的速度通过连接中间件自动将 .scss 文件本地编译为 css $ yarn add -D sass-loader # Compiles Sass to CSS $ yarn add -D css-loader # The css-loader interprets @import and url() like import/require() and will resolve them. $ yarn add -D style-loader # Inject CSS into the DOM.
Примечание: sass разработан на основе языка Ruby, поэтому перед установкой sass необходимо установить Ruby. (Примечание: Ruby не нужно устанавливать под Mac!)
Зачем вам нужен node-sass : Из-за sass-loaderpeerDependenciesЗаявлено, что зависит от node-sass, поэтому его нужно предварительно установить, иначе будет предупреждение.
-
Конфигурация: измените webpack.config.js, чтобы расширить возможности парсинга css/sass.
... moduele.exports = function () { ... module: { rules: [ ... + { + test: /\.(sa|sc|c)ss$/, + exclude: /node_modules/, + use: [ + { + loader: 'style-loader' + }, + { + loader: 'css-loader', + options: { + sourceMap: !IS_PROD + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: !IS_PROD + } + } + ] + } ] } ... } ...
-
Добавлены файлы стилей src/index.scss и style/global.css.
$ cd src && touch index.scss $ mkdir style && cd style $ touch global.css && touch reset.css
// src/style/reset.css # reset 重置浏览器初始样式,具体样式参见项目 src/style/reset.css // src/style/global.css @import url('./reset.css'); // src/index.scss .app { background-color: red; }
-
Измените src/index.js, чтобы импортировать таблицу стилей.
import { hot } from 'react-hot-loader'; import React, { useState } from 'react'; import ReactDom from 'react-dom'; + import './style/global.css'; + import './index.scss'; const App = hot(module)(() => { const [title, setTitle] = useState('hello, world!'); const reversedTitle = () => setTitle( title .split('') .reverse() .join('') ); return ( - <div> + <div className='app'> <h1>{title}</h1> <button type='button' onClick={reversedTitle}> reversed title! </button> </div> ); }); ReactDom.render(<App />, document.getElementById('root'));
-
запустить проект
$ yarn server # 结果: $ cross-env NODE_ENV=development webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully.
Открыть
http://localhost:3000/
, как вы и написали, появляется красный фон. Попытайся! -
Проблемы и улучшения 🤔
- Отсутствие плагинов для автоматического управления префиксами браузера, парсинга
CSS
файл и добавьте префикс браузера кCSS
в содержании;postcss/autoprefixer
- При наличии большого количества файлов стилей компонентов во избежание конфликтов стилей можно использовать
css-modules
Для решения этой проблемы. Конечно, вы также можете обойти эту проблему, приняв строгие соглашения об именах, такие как: БЭМ.
Тогда вперёд! 💪
- Отсутствие плагинов для автоматического управления префиксами браузера, парсинга
14. CSS-Modulesа такжеautoprefixer
-
Установить
$ yarn add - D postcss-loader # 用于webpack的Loader以使用PostCSS处理CSS $ yarn add -D autoprefixer # Parse CSS and add vendor prefixes to rules by Can I Use
-
Создайте новый файл конфигурации postcss
$ touch postcss.config.js # 新建 postcss 配置文件 # starter/postcss.config.js 添加 autoprefixer 插件 module.exports = { plugins: { autoprefixer: {}, } };
-
Добавить конфигурацию webpack postcss
... moduele.exports = function () { ... module: { rules: [ ... { - test: /\.(sa|sc|c)ss$/, + test: /\.(sa|sc)ss$/, exclude: /node_modules/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', + options: { + sourceMap: !IS_PROD, + importLoaders: 2, // 启用/禁用或设置在CSS加载程序之前应用的加载程序的数量 + modules: { + context: path.resolve(__dirname, 'src'), // 允许为本地标识符名称重新定义基本的加载程序上下文。 + localIdentName: '[name]__[local]-[hash:base64:5]' // 使用 localIdentName 查询参数配置生成类名 + } + } }, + { + loader: 'postcss-loader' + } { loader: 'sass-loader', options: { sourceMap: !IS_PROD } } ] }, + { + test: /\.css$/, + exclude: /node_modules/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + sourceMap: !IS_PROD + } + }, + 'post-loader' + ] + } ] } ... } ...
postcss
: одно использованиеJavaScript
конвертироватьCSS
Инструмент
css-loader
поставкаCSS
Модули и их конфигурация -
Изменить запись имени класса src/index.js
import { hot } from 'react-hot-loader'; import React, { useState } from 'react'; import ReactDom from 'react-dom'; import './style/global.css'; - import './index.scss'; + import styles from './index.scss'; const App = hot(module)(() => { const [title, setTitle] = useState('hello, world!'); const reversedTitle = () => setTitle( title .split('') .reverse() .join('') ); return ( - <div className='app'> + <div className={styles.app}> <h1>{title}</h1> <button type='button' onClick={reversedTitle}> reversed title! </button> </div> ); }); ReactDom.render(<App />, document.getElementById('root'));
-
запустить проект
$ yarn server # 结果 $ cross-env NODE_ENV=development webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/gt/LEMON/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully.
Открыть
http://localhost:3000/
Проверьте, так ли это, как вы написали!
15. Идем дальше и создаем наше приложениеyarn build
Пакет
$ yarn build
# 结果
$ cross-env NODE_ENV=production webpack --color --progress
Hash: 4f40eeb2a231c73dacd9
Version: webpack 4.41.2
Time: 4142ms
Built at: 2019-10-21 10:54:37
Asset Size Chunks Chunk Names
bundle.js 136 KiB 0 [emitted] main
Entrypoint main = bundle.js
[5] ./src/index.scss 498 bytes {0} [built]
[7] ./src/index.js 1.57 KiB {0} [built]
[8] (webpack)/buildin/harmony-module.js 573 bytes {0} [built]
[13] ./src/style/global.css 457 bytes {0} [built]
[14] ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/src!./src/style/global.css 237 bytes {0} [built]
[15] ./node_modules/css-loader/dist/cjs.js!./src/style/reset.css 1.28 KiB {0} [built]
[16] ./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src!./node_modules/sass-loader/dist/cjs.js!./src/index.scss 238 bytes {0} [built]
+ 11 hidden modules
✨ Done in 5.90s.
Мы видим, что это касается только одного
bundle.js
Этого явно недостаточно. Далее делаем несколько изменений!
Управление выводом
До сих пор мы вручную импортировали все ресурсы в файл index.html, однако по мере роста приложения и после того, как вы начнете использовать hash] в именах файлов и экспортировать несколько пакетов, если вы продолжите управлять файлом index.html вручную, станет трудный.
-
Изменить веб-пакет - вывод
const path = require('path'); const webpack = require('webpack'); const IS_PROD = process.env.NODE_ENV === 'production'; ... output: { path: path.resolve(__dirname, 'dist'), - publicPath: '/', + publicPath: IS_PROD ? '/starter/' : '/', // 公共路径 - filename: 'bundle.js' + filename: IS_PROD ? '[name].[contenthash:8].js' : '[name].js', // 输出文件的文件名 + chunkFilename: IS_PROD ? 'chunks/[name].[contenthash:8].js' : '[name].js', // 非入口(non-entry) chunk 文件的名称 }, ...
-
HtmlWebpackPlugin
$ yarn add -D html-webpack-plugin # 安装插件
<!-- starter/webpack.config.js --> const path = require('path'); const webpack = require('webpack'); const IS_PROD = process.env.NODE_ENV === 'production'; + const HtmlWebpackPlugin = require('html-webpack-plugin') ... - plugins: [] + plugins: [ + new HtmlWebpackPlugin({ + title: 'Starter', + filename: 'index.html', + template: path.resolve(__dirname, 'public/index.html'), + minify: IS_PROD + ? { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true, + collapseBooleanAttributes: true, + removeScriptTypeAttributes: true + } + : {} + }), + ] ...
-
Изменить общедоступный/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="./favicon.ico" /> <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="This is a react application built from scratch with JavaScript, away from the cli tool." /> - <title>Starter</title> + <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> - <script src="bundle.js"></script> </body> </html>
-
С приведенной выше конфигурацией давайте посмотрим на эффект
$ yarn build # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: 6bb93a13b6a8a7926f58 Version: webpack 4.41.2 Time: 4418ms Built at: 2019-10-21 11:47:05 Asset Size Chunks Chunk Names index.html 553 bytes [emitted] main.2f781ad1.js 136 KiB 0 [emitted] [immutable] main Entrypoint main = main.2f781ad1.js [5] ./src/index.scss 498 bytes {0} [built] [7] ./src/index.js 1.57 KiB {0} [built] [8] (webpack)/buildin/harmony-module.js 573 bytes {0} [built] [13] ./src/style/global.css 457 bytes {0} [built] [14] ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/src!./src/style/global.css 237 bytes {0} [built] [15] ./node_modules/css-loader/dist/cjs.js!./src/style/reset.css 1.28 KiB {0} [built] [16] ./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src!./node_modules/sass-loader/dist/cjs.js!./src/index.scss 238 bytes {0} [built] + 11 hidden modules Child html-webpack-plugin for "index.html": 1 asset Entrypoint undefined = index.html [0] ./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html 858 bytes {0} [built] [2] (webpack)/buildin/global.js 472 bytes {0} [built] [3] (webpack)/buildin/module.js 497 bytes {0} [built] + 1 hidden module ✨ Done in 6.04s.
<!-- starter/dist/index.html --> <!DOCTYPE html><html lang=en><head><meta charset=utf-8><link rel=icon href=./favicon.ico><meta name=viewport content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><meta name=theme-color content=#000000><meta name=description content="This is a react application built from scratch with JavaScript, away from the cli tool."><title>React App TS</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id=root></div><script src=/starter/main.2f781ad1.js></script></body></html>
Примечание. Если вы внимательно посмотрите на наш вывод, вы обнаружите
main.2f781ad1.js size=136KiB
, а наш код очень маленький, если вы откроете файл, то обнаружите, что он содержитreact.production.min.js
babel
Необходимые вспомогательные функции и т.д.
разделение кода
-
mini-css-extract-plugin- отдельный css-код
По умолчанию webpack помещает css и js в один файл, плагин извлекает CSS в отдельные файлы. Он создает файл CSS для каждого файла JS, содержащего CSS.
Почему отдельно?webpack-contrib/mini-css-extract-plugin
$ yarn add -D mini-css-extract-plugin # 安装
<!-- starter/webpack.config.js --> ... + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, { test: /\.(sa|sc)ss$/, exclude: /node_modules/, use: [ { - loader: 'style-loader' + loader: IS_PROD ? MiniCssExtractPlugin.loader : 'style-loader', + options: IS_PROD ? { publicPath: '../' } : {} }, { loader: 'css-loader', options: { sourceMap: false, importLoaders: 2, modules: { context: path.resolve(__dirname, 'src'), localIdentName: '[name]__[local]-[hash:base64:5]' } } }, { loader: 'postcss-loader' }, { loader: 'sass-loader' } ] }, { test: /\.css$/, exclude: /node_modules/, - use: ['style-loader', 'css-loader', 'postcss-loader'] + use: [ + { + loader: IS_PROD ? MiniCssExtractPlugin.loader : 'style-loader', + options: IS_PROD ? { publicPath: '../' } : {} + }, + 'css-loader', + 'postcss-loader' + ] } ] }, + plugins: [ ..., + new MiniCssExtractPlugin({ + filename: IS_PROD ? 'css/[name].[contenthash:8].css' : 'css/[name].css', + chunkFilename: IS_PROD ? 'css/[name].[contenthash:8].css' : 'css/[name].css' + }) ]
$ yarn build $ cross-env NODE_ENV=production webpack --color --progress Hash: 95fccf0e0844c2df588f Version: webpack 4.41.2 Time: 4416ms Built at: 2019-10-21 13:56:23 Asset Size Chunks Chunk Names ! css/main.f9cee851.css 1.08 KiB 0 [emitted] [immutable] main index.html 605 bytes [emitted] ! main.ced0f821.js 131 KiB 0 [emitted] [immutable] main Entrypoint main = css/main.f9cee851.css main.ced0f821.js
После многократных упаковок мы нашли во многих местах много последних файлов результатов, которые явно не выдерживают w(゚Д゚)w; мы хотим удалить папку, сгенерированную предыдущей сборкой, перед каждой сборкой.
-
clean-webpack-pluginсодержать каталог в чистоте
Используется для удаления папки сборки перед сборкой
$ yarn add -D clean-webpack-plugin # 安装
<!-- starter/webpack.config.js --> ... + const { CleanWebpackPlugin } = require('clean-webpack-plugin'); ... plugins: [ ..., + new CleanWebpackPlugin() ] ...
Попробуйте👀, я почистил (。・∀・)ノ゙Try it!
предотвратить повторение
-
optimization.splitChunksИзвлечь общие зависимые модули в существующий фрагмент записи
<!-- starter/webpack.config.js --> ... module.exports = function () { const baseConfig = { ... } + if (IS_PROD) { + baseConfig.optimization = { + minimizer: [ + // Automatically split vendor and commons + splitChunks: { + chunks: 'all' + } + ] + } + } return baseConfig; }
$ yarn build # 打包查看效果 # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: ebe27d1c4dc54ff22c4b Version: webpack 4.41.2 Time: 4470ms Built at: 2019-10-21 14:28:59 Asset Size Chunks Chunk Names ! chunks/vendors~main.f501917c.js 129 KiB 1 [emitted] [immutable] vendors~main css/main.f9cee851.css 1.08 KiB 0 [emitted] [immutable] main ! index.html 667 bytes [emitted] ! main.76c9ecec.js 2.54 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.f501917c.js css/main.f9cee851.css main.76c9ecec.js # 注意:如果你仔细看 chunks/vendors~main.f501917c.js 你会发现 与 react 相关的库 #(react.production.min.js、react-dom.production.min.js、scheduler.production.min.js)和你代 # 码所引用的公共库都将被提取出来,防止重复引用。
webpack 4: Code Splitting, chunk graph and the splitChunks optimization
-
@babel/plugin-transform-runtimeПлагин, который повторно использует вспомогательный код, внедренный Babel, для экономии размера кода.
$ yarn add -D @babel/plugin-transform-runtime
<!-- starter/postcss.config.js --> { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ + "@babel/plugin-transform-runtime", "react-hot-loader/babel" ] }
$ yarn build # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: 6425898f896ed7244e2b Version: webpack 4.41.2 Time: 4510ms Built at: 2019-10-21 15:18:47 Asset Size Chunks Chunk Names ! chunks/vendors~main.e9e35553.js 130 KiB 1 [emitted] [immutable] vendors~main css/main.f9cee851.css 1.08 KiB 0 [emitted] [immutable] main index.html 667 bytes [emitted] ! main.5fb316df.js 2.07 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.e9e35553.js css/main.f9cee851.css main.5fb316df.js # 可以比对上次构建结果,主文件减少了一些。
-
webpack.DefinePluginПозволяет создать глобальную константу, которую можно настроить во время компиляции.
Плагины могут настраивать некоторые глобальные переменные, которые будут заменены теми переменными, на которые есть ссылки в коде во время сборки. Например: NODE_ENV (часто используется для производственных сред и сред разработки). Если ведение журнала выполняется в сборке разработки, а не в сборке выпуска, можно использовать глобальную константу, чтобы решить, вести журнал или нет. Для этого и нужен DefinePlugin, настройте его и можете забыть о правилах для dev и production сборок.
<!-- starter/webpack.config.js --> ... plugins: [ ..., + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + } + }) ] ...
Здесь, если ваш код не дифференцируется в среде и не выполняет специфическую обработку (удаление кода в среде разработки), размер пакета останется неизменным.
minify JavaScript / css
-
$ yarn add -D uglifyjs-webpack-plugin
<!-- starter/webpack.config.js --> ... + const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); ... if (IS_PROD) { baseConfig.optimization = { + minimizer: [ + new UglifyjsWebpackPlugin({ + exclude: /node_modules/, + sourceMap: false, // 使用源映射将错误消息位置映射到模块(这会减慢编译速度)。如果您使用自己的缩小功能,请阅读缩小部分以正确处理源地图。 + cache: true, // 启用文件缓存 + parallel: true // 使用多进程并行运行可提高构建速度。并发运行的默认数量:os.cpus().length - 1. + }) + ], splitChunks: { chunks: 'all', } }; }
$ yarn build # 打包验证 ✅ # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: 3f450244bccc719560c5 Version: webpack 4.41.2 Time: 2209ms Built at: 2019-10-21 16:25:18 Asset Size Chunks Chunk Names ! chunks/vendors~main.1a122e64.js 129 KiB 1 [emitted] [immutable] vendors~main css/main.f9cee851.css 1.08 KiB 0 [emitted] [immutable] main index.html 667 bytes [emitted] main.e82008bc.js 2.07 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.1a122e64.js css/main.f9cee851.css main.e82008bc.js
Уведомление:
uglifyjs-webpack-plugin v2.x
версия, основанная наuglify-js
, не может поддерживатьES6
компрессияСсылаться на:Почему webpack4 по умолчанию поддерживает сжатие синтаксиса ES6?
-
мы используем
terser-webpack-plugin
заменятьuglifyjs-webpack-plugin
$ yarn add -D terser-webpack-plugin
<!-- starter/webpack.config.js --> - const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); + const TerserPlugin = require('terser-webpack-plugin'); if (IS_PROD) { baseConfig.optimization = { minimizer: [ - new UglifyjsWebpackPlugin({ - exclude: /node_modules/, - sourceMap: false, - cache: true, - parallel: true - }), + new TerserPlugin({ + // Terser minify options. + terserOptions: { + parse: { + // We want terser to parse ecma 8 code. However, we don't want it + // to apply any minification steps that turns valid ecma 5 code + // into invalid ecma 5 code. This is why the 'compress' and 'output' + // sections only apply transformations that are ecma 5 safe + ecma: 8, + }, + compress: { + ecma: 5, + // display warnings when dropping unreachable code or unused declarations etc. + warnings: false, + // apply certain optimizations to binary nodes + // Disabled because of an issue with Uglify breaking seemingly valid code: + // Pending further investigation: https://github.com/mishoo/UglifyJS2/issues/2011 + comparisons: false, + // inline calls to function with simple/return statement: + // Disabled because of an issue with Terser breaking valid code: + // Pending further investigation: https://github.com/terser-js/terser/issues/120 + inline: 2, // inline functions with arguments + }, + mangle: { + // Pass true to work around the Safari 10 loop iterator bug "Cannot declare a let variable twice". + // See also: the safari10 output option. + safari10: true, + }, + // Added for profiling in devtools + keep_classnames: true, + keep_fnames: true, + output: { + ecma: 5, + // pass true or "all" to preserve all comments, "some" to preserve some comments, + // a regular expression string (e.g. /^!/) or a function. + comments: false, + // escape Unicode characters in strings and regexps (affects directives with non-ascii characters becoming invalid) + // Turned on because emoji and regex is not minified properly using default + ascii_only: true, + }, + }, + // Use multi-process parallel running to improve the build speed. + //Default number of concurrent runs: os.cpus().length - 1. + parallel: true, + cache: true, // Enable file caching + }), ], splitChunks: { chunks: 'all', } }; }
$ yarn build $ cross-env NODE_ENV=production webpack --color --progress Hash: dbf5243d5591e4ac0268 Version: webpack 4.41.2 Time: 2461ms Built at: 2019-10-21 17:52:26 Asset Size Chunks Chunk Names ! chunks/vendors~main.ae62441b.js 130 KiB 1 [emitted] [immutable] vendors~main ! chunks/vendors~main.ae62441b.js.LICENSE 790 bytes [emitted] css/main.f9cee851.css 1.08 KiB 0 [emitted] [immutable] main index.html 667 bytes [emitted] ! main.2130b172.js 2.52 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.ae62441b.js css/main.f9cee851.css main.2130b172.js
-
optimize-css-assets-webpack-plugin- Оптимизация/уменьшение активов CSS
$ yarn add -D optimize-css-assets-webpack-plugin # 压缩 CSS $ yarn add -D postcss-safe-parser # 查找并修复 CSS 语法错误
<!-- starter/webpack.config.js --> ... + const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); + const SafePostCssParser = require('postcss-safe-parser'); if (IS_PROD) { baseConfig.optimization = { minimizer: [ ... + new OptimizeCSSAssetsPlugin({ + // The options passed to the cssProcessor, defaults to {} + // cssProcessor: The CSS processor used to optimize \ minimize the CSS, defaults to cssnano. + // This should be a function that follows cssnano.process interface + // (receives a CSS and options parameters and returns a Promise). + cssProcessorOptions: { + parser: SafePostCssParser, + map: false, + }, + }) ], ... }; } ...
$ yarn build # 打包实验 ✅ # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: dbf5243d5591e4ac0268 Version: webpack 4.41.2 Time: 3543ms Built at: 2019-10-21 20:17:23 Asset Size Chunks Chunk Names chunks/vendors~main.ae62441b.js 130 KiB 1 [emitted] [immutable] vendors~main chunks/vendors~main.ae62441b.js.LICENSE 790 bytes [emitted] ! css/main.f9cee851.css 869 bytes 0 [emitted] [immutable] main index.html 667 bytes [emitted] main.2130b172.js 2.52 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.ae62441b.js css/main.f9cee851.css main.2130b172.js
Внешнее расширение (externals)
Исключите зависимости из выходного пакета; предотвратите упаковку некоторых импортированных пакетов в пакеты, а вместо этого извлеките эти внешние зависимости во время выполнения.
-
CDN — этот шаг можно пропустить
<!-- starter/webpack.config.js --> ... module.exports = function() { const baseConfig = { ... resolve: { alias: { 'react-dom': '@hot-loader/react-dom' // react-hot-loader 兼容 hook 写法 } }, + externals: { + react: 'React', + 'react-dom': 'ReactDOM' + }, ... } ... <!-- starter/public/index.html --> ... <div id="root"></div> + <script crossorigin src="https://unpkg.com/react@16.10.2/umd/react.production.min.js"></script> + <script crossorigin src="https://unpkg.com/@hot-loader/react-dom@16.10.2/umd/react-dom.production.min.js"></script> ...
$ yarn build $ cross-env NODE_ENV=production webpack --color --progress Hash: 6bb2de2632bdaf2dc081 Version: webpack 4.41.2 Time: 2557ms Built at: 2019-10-21 21:35:38 Asset Size Chunks Chunk Names css/main.34dd0d40.css 869 bytes 0 [emitted] [immutable] main index.html 811 bytes [emitted] main.da7fbe78.js 3.85 KiB 0 [emitted] [immutable] main Entrypoint main = css/main.34dd0d40.css main.da7fbe78.js
1. Что такое CDN? Каковы преимущества использования CDN?
2. Несколько публичных библиотек CDN:cdnjs,jsdelivr,unpkg
3. Для повышения скорости доступа лучше всего исключить из выходного бандла зависимости нечасто обновляемых библиотек классов фронтенда, таких как react, react-dom, axios, moment и т.д.
4. Напоминаем, что лучше всего получить его самостоятельно, и всегда безопасно использовать свой собственный 🤡
Каталог проекта
└── starter
+ ├── dist
+ │ └── chunks
+ │ │ ├── vendors~main.ae62441b.js
+ │ │ └── vendors~main.ae62441b.js.LICENSE
+ ├── css
+ │ │ └── main.f9cee851.css
+ │ ├── index.html
+ │ └── main.2130b172.js
├── node_modules
├── public
│ ├── favicon.ico
│ └── index.html
├── src
+ │ └── style
+ │ | ├── global.css
+ │ | └── reset.css
| ├── index.js
+ │ ├── index.scss
+ ├── postcss.config.js
├── webpack.config.js
├── package.json
├── README.md
├── LICENSE
└── yarn.lock
сценический эпилог
- Пока грубо обсуждался весь процесс строительства и моменты оптимизации, сделанные в процессе строительства.Конечно, есть еще некоторые недочеты. 📚
- Для завершения проекта предстоит еще много работы, так что вперед! 🔥👇🔥
16. Импорт маршрутов
Для интерфейсных одностраничных приложений маршрутизация имеет важное значение.В настоящее время основные фреймворки имеют соответствующие подключаемые модули маршрутизации, которые представлены здесь вместе с выбранным фреймворком.react-router-dom
-
Установить
$ yarn add react-router-dom
-
Создайте новую папку конфигурации маршрутизации
$ cd src && mkdir router # 新建 router 文件夹 $ cd router $ touch index.js # 新建路由配置文件 $ touch list.js # 新建路由表文件
-
Написать конфигурацию маршрутизации и таблицу маршрутизации
-
Конфигурация маршрутизации — src/router/index.js
import React from 'react'; import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; import routes from './list'; function RouterView(route) { return ( <Route path={route.path} render={(props) => { if (route.redirect) { return <Redirect to={route.redirect} />; } return ( <route.component {...props} render={() => ( <Switch> {route.routes.map((children) => ( <RouterView key={children.path} {...children} /> ))} </Switch> )} /> ); }} /> ); } export default function Router() { return ( <BrowserRouter> <Switch> {routes.map((route) => ( <RouterView key={route.path} {...route} /> ))} </Switch> </BrowserRouter> ); }
Это написано в соответствии с документом конфигурации маршрутизации и используется только для DEMO; для получения подробной информации см.react-router: Route Config
-
Таблица маршрутизации — src/router/list.js
import Github from '../views/Github/Github'; import Setting from '../views/Setting/Setting'; const routes = [ { path: '/', exact: true, redirect: '/github' }, { path: '/github', component: Github, }, { path: '/setting', component: Setting, } ]; export default routes;
Вы можете создать любое имя здесь 🙄
-
-
Создайте новую настройку, страницу GitHub и напишите
# 新建 Setting、GitHub 页面 $ cd src/views $ mkdir Github && cd Github $ touch Github.js && touch Github.scss $ cd .. $ mkdir Setting && cd Setting $ touch Setting.js && touch Setting.scss $ cd ..
// starter/Github/Github.js import React from 'react'; import { useHistory } from 'react-router-dom'; import styles from './Github.scss'; function Github() { const history = useHistory(); function handleClick() { history.push('/setting'); } return ( <div className={`${styles.root}`}> <h1>Github</h1> <div className={`${styles.bg} ${styles.wh}`}> {`当前环境: ${process.env.NODE_ENV}`} </div> <button type='button' onClick={handleClick}> Go setting </button> </div> ); } export default Github; // starter/Setting/Setting.js import React from 'react'; import { useHistory } from 'react-router-dom'; import styles from './Setting.scss'; function Setting() { const history = useHistory(); function handleClick() { history.push('/github'); } return ( <div className={`${styles.root}`}> <h1>Setting</h1> <div className={`${styles.bg} ${styles.wh}`}> {`当前环境: ${process.env.NODE_ENV}`} </div> <button type='button' onClick={handleClick}> Go github </button> </div> ); } export default Setting;
// starter/Setting/Setting.scss .root { .wh { width: 200px; height: 180px; } .bg { text-align: center; line-height: 180px; background: no-repeat url('~assets/images/logo.png'); } } // starter/Github/Github.scss .root { .wh { width: 200px; height: 200px; } .bg { text-align: center; line-height: 200px; background: no-repeat url('~assets/images/logo.png'); } }
Поскольку стиль вводит изображения, мы создаем новую папку хранения ресурсов для хранения этих ресурсов.
$ cd src && mkdir assets $ cd assets && mkdir images $ cd images $ copy logo.png # 这里的图标是官网搂过来的,🤣
-
Измените наш основной файл src/index.js
import { hot } from 'react-hot-loader'; - import React, { useState } from 'react'; + import React from 'react'; import ReactDom from 'react-dom'; import './style/global.css'; - import styles from './index.scss'; + import Router from './router/index'; - const App = hot(module)(() => { - const reversedTitle = () => - setTitle( - title - .split('') - .reverse() - .join('') - ); - return ( - <div className={styles.app}> - <h1>{title}</h1> - <button type='button' onClick={reversedTitle}> - reversed title! - </button> - </div> - ); - }); + const App = hot(module)(() => ( + <div className='app'> + <Router /> + </div> + )); ReactDom.render(<App />, document.getElementById('root'));
-
Теперь все готово, но прежде чем приступить к проекту, несколько моментов
- (Мы представили изображения на странице, и по мере роста проекта мы можем добавить значки шрифтов, аудио и другие файлы). Здесь мы используем веб-пакет, чтобы помочь нам управлять этими ресурсами унифицированным образом.
- По мере углубления проекта структура каталогов будет становиться все более сложной.
webpack
-resolve.alias
, создавать псевдонимы для импорта или требовать, чтобы упростить импорт модулей.
Сделайте некоторые улучшения ️ ⚓️
17. Управление ресурсами,оптимизацияРазрешение модуля
-
Разрешение модуля
<!-- starter/webpack.config.js --> ... resolve: { alias: { 'react-dom': '@hot-loader/react-dom', // react-hot-loader 兼容 hook 写法 + '@': path.resolve(__dirname, 'src'), + assets: path.resolve(__dirname, 'src/assets'), + style: path.resolve(__dirname, 'src/style') } }, ...
-
Управление ресурсами
# 安装 $ yarn add -D url-loader # 将文件转换为 base64 URI。 $ yarn add -D file-loader # 将文件上的 import/require() 解析为 url,并将该文件发射到输出目录中。
<!-- starter/webpack.config.js --> module: { rules: [ ... + { + test: /\.(png|jpe?g|gif|webp)(\?.*)?$/, // 匹配这些格式的图片 + use: [ + { + loader: 'url-loader', + options: { + limit: 4096, // 文件大小等于或大于限制,则将使用 file-loader。 + fallback: { + loader: 'file-loader', + options: { + name: 'images/[name].[hash:8].[ext]' + } + } + } + } + ] + }, + { + test: /\.(svg)(\?.*)?$/, + use: [ + { + loader: 'file-loader', + options: { + name: 'svg/[name].[hash:8].[ext]' + } + } + ] + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: 4096, + fallback: { + loader: 'file-loader', + options: { + name: 'fonts/[name].[hash:8].[ext]' + } + } + } + } + ] + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + use: [ + { + loader: 'url-loader', + options: { + limit: 4096, + fallback: { + loader: 'file-loader', + options: { + name: 'media/[name].[hash:8].[ext]' + } + } + } + } + ] + } + ] }
Здесь мы не вводим
svg
, файл значка шрифта, аудиофайл, но здесь для удобства дальнейшего углубления мы просто добавляем его конфигурацию. -
Хорошо, давайте начнем наш проект. Попытайся!
$ yarn server
-
Пакет
$ yarn build # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: fb9c0cc487e7845fd915 Version: webpack 4.41.2 Time: 3533ms Built at: 2019-10-23 11:42:44 Asset Size Chunks Chunk Names chunks/vendors~main.64d1203b.js 160 KiB 1 [emitted] [immutable] vendors~main chunks/vendors~main.64d1203b.js.LICENSE 1.01 KiB [emitted] ! css/main.b7d00a9e.css 1.19 KiB 0 [emitted] [immutable] main images/logo.581fa1d8.png 8.38 KiB [emitted] index.html 667 bytes [emitted] ! main.cfa18e59.js 3.95 KiB 0 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.64d1203b.js css/main.b7d00a9e.css main.cfa18e59.js
-
Проблемы и моменты, требующие оптимизации
- По мере увеличения сложности проекта, когда приложение будет упаковано и построено, пакет JavaScript станет очень большим, что повлияет на загрузку страницы. Было бы более эффективно, если бы мы могли разделить компоненты, соответствующие разным маршрутам, на разные блоки кода, а затем загружать соответствующие компоненты при доступе к маршруту.
18. Отложенная загрузка маршрута@loadable/component
Примечание. Использование этого подключаемого модуля для разделения кода вашего приложения может помочь вам «лениво загружать» контент, который нужен текущему пользователю, что может значительно повысить производительность вашего приложения. Хотя это не уменьшает общий размер кода приложения, вы можете избежать загрузки кода, который никогда не понадобится пользователю, и уменьшить объем кода, который необходимо загрузить при начальной загрузке.
-
Установить
# 当然你也可以选择,React.lazy 和 Suspense,但他们还不支持服务端渲染。这里直接选择功能更加强大的 @loadable/component $ yarn add @loadable/component
-
Изменить таблицу маршрутизации
! <!-- src/router/list --> - import Github from '@/views/Github/Github'; - import Setting from '@/views/Setting/Setting'; + import React from 'react'; + import loadable from '@loadable/component'; + const Github = import(/* webpackChunkName: "github" */ '@/views/Github/Github.js'); + const Setting = import/* webpackChunkName: "setting" */ ('@/views/Setting/Setting.js'); + const AsyncComponent = (loader) => loadable(loader, { fallback: <h3>Loading...</h3> }); const routes = [ { path: '/', exact: true, redirect: '/github' }, { path: '/github', - component: Github + component: AsyncComponent(() => Github) }, { path: '/setting', - component: Setting + component: AsyncComponent(() => Setting) } ]; export default routes;
-
Упакуйте наше приложение и посмотрите на результаты разделения кода.
$ yarn build # 结果 $ cross-env NODE_ENV=production webpack --color --progress Hash: b93be70da668f4dff43b Version: webpack 4.41.2 Time: 6077ms Built at: 2019-10-23 16:21:08 Asset Size Chunks Chunk Names ! chunks/github.45dc6c0d.js 634 bytes 0 [emitted] [immutable] github ! chunks/setting.316d765f.js 637 bytes 2 [emitted] [immutable] setting chunks/vendors~main.a51021eb.js 164 KiB 3 [emitted] [immutable] vendors~main chunks/vendors~main.a51021eb.js.LICENSE 1.01 KiB [emitted] ! css/github.8de607a6.css 191 bytes 0 [emitted] [immutable] github ! css/setting.1a0bfbdd.css 195 bytes 2 [emitted] [immutable] setting css/main.c1fb052e.css 830 bytes 1 [emitted] [immutable] main images/logo.581fa1d8.png 8.38 KiB [emitted] index.html 667 bytes [emitted] main.02bdd0e7.js 4.96 KiB 1 [emitted] [immutable] main Entrypoint main = chunks/vendors~main.a51021eb.js css/main.c1fb052e.css main.02bdd0e7.js
-
Каталог проекта
└── starter + ├── dist + │ └── chunks + │ │ ├── github.45dc6c0d.js + │ │ ├── setting.316d765f.js + │ │ ├── vendors~main.a51021eb.js + │ │ └── vendors~main.a51021eb.js.LICENSE + ├── css + │ │ ├── 0.8de607a6.css + │ │ ├── 2.1a0bfbdd.css + │ │ └── main.c1fb052e.css + │ ├── images + │ │ └── logo.581fa1d8.png │ ├── index.html + │ └── main.02bdd0e7.js ├── node_modules ├── public │ ├── favicon.ico │ └── index.html ├── src + │ ├── assets + │ │ └── images + │ │ └── logo.png + │ ├── router + │ │ ├── index.js + │ │ └── list.js │ ├── style │ | ├── global.css │ | └── reset.css + | ├── views + │ | ├── Github + │ | │ ├── Github.js + │ | │ └── Github.scss + │ | └── Setting + │ | ├── Setting.js + │ | └── Setting.scss - │ ├── index.scss | └── index.js ├── postcss.config.js ├── webpack.config.js ├── package.json ├── README.md ├── LICENSE └── yarn.lock
На данный момент мы добавили функцию маршрутизации, продолжим доработку! 🚘
19. Стандарты кодирования
Пока что количество кода в нашем проекте увеличивается, и написанный код все еще может иметь некоторые потенциальные проблемы (этого трудно избежать); другой, большой проект часто поддерживается командой, но стиль кода членов команды отличается. не совсем так. Исходя из этого, нам нужен инструмент для решения этих болевых точек.
-
инструмент
-
eslint:Обычно используется для проверки распространенных ошибок кода JavaScript, а также может выполнять проверки стиля кода.
-
stylelint:Мощный современный линтер, помогающий избежать ошибок и обеспечить соблюдение соглашений о стилях.
-
prettier:Инструмент форматирования кода, обеспечивающий единообразие стилей за счет анализа кода и его повторной печати с использованием собственных правил, при необходимости заключая код в оболочку.
После обсуждения важности соглашений о написании кода и наборов инструментов давайте посмотрим, как их применять в проекте.
-
настроитьeslint
-
Установить
$ yarn add -D eslint # eslint $ yarn add -D babel-eslint # 一个对 Babel 解析器的包装,使其能够与 ESLint 兼容 $ yarn add -D eslint-plugin-react # 检测 react 代码 $ yarn add -D eslint-plugin-react-hooks # 用于检测 hook 规则 $ yarn add -D eslint-plugin-jsx-a11y # 用于检测 jsx 规范 $ yarn add -D eslint-plugin-import # ESLint 插件,带有有助于验证正确导入的规则。 $ yarn add -D eslint-import-resolver-webpack # 用于 eslint-plugin-import的 Webpack-literate 模块解析插件。
-
Создайте новый файл конфигурации eslint
$ touch .eslintrc # eslint 配置文件 $ touch .eslintignore # eslint 忽略检测配置文件
<!-- starter/.eslintrc --> { "root": true, "env": { "es6": true, "browser": true, }, "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:jsx-a11y/recommended"], "parser": "babel-eslint", "plugins": ["react", "jsx-a11y", "react-hooks", "import"], "rules": { "semi": ["error", "always"], "quotes": ["error", "single"], "camelcase": [0, { "properties": "never" }], "no-console": [2, { "allow": ["warn", "error"] }], "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "react/jsx-props-no-spreading": "off", "jsx-a11y/click-events-have-key-events": "off", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/no-unused-prop-types": "off" }, "settings": { "react": { "version": "16.10.2" }, "import/resolver": "webpack" }, "globals": { "process": true, "module": true } } <!-- starter/.eslintignore --> node_modules dist
-
Инструкции по настройке
-
"eslint:recommended"
Включить рекомендуемые правила -
"plugin:react/recommended"
Плагин экспортирует предлагаемые конфигурации для обеспечения соблюдения передового опыта React. -
"babel-eslint"
Оболочка парсера Babel, чтобы сделать его совместимым с ESLint. - правила: Пользовательские правила, которые могут переопределить расширенную конфигурацию.
-
eslint-plugin-import
Этот плагин предназначен для поддержки проверки синтаксиса импорта/экспорта ES2015+ (ES6+) и предотвращения проблем с путями к файлам с ошибками и именами импорта. -
"import/resolver": "webpack"
: Решить проблему, вызванную конфигурацией псевдонима веб-пакета.eslint-plugin-import
сообщить об ошибке. - Эта конфигурация представляет собой простую подробную настройку, пожалуйста, обратитесь кConfiguring ESLint
Примечание. Конфигурация eslint должна согласовать набор действующих спецификаций в соответствии с внутренней командой.
-
-
Измените package.json, чтобы создать новую команду быстрого доступа.
<!-- starter/package.json --> "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", "build": "cross-env NODE_ENV=production webpack --color --progress", + "lint:script": "eslint --ext '.js,.jsx' src", + "lint-fix:script": "npm run lint:script -- --fix" },
-
Выполните команду, чтобы увидеть, есть ли какие-либо нарушения
$ yarn lint:script # 执行 lint $ yarn lint-fix:script # 执行 lint 并自动修复 # 结果, 如果存在错误,则根据文档自行修复。 $ npm run lint:script -- --fix > starter@1.0.0 lint:script /Users/gt/LEMON/starter > eslint --ext '.js,.jsx' src "--fix" ✨ Done in 2.59s.
-
Кроме того, мы хотим каждый раз выполнять код форматирования lint перед транспилированием файлов js и jsx.
# 安装 $ yarn add -D eslint-loader # eslint loader (for webpack)
// 修改 webpack.config.js 配置 ... module: { rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + include: path.resolve(__dirname, 'src'), + enforce: 'pre', + use: [ + { + loader: 'eslint-loader', + options: { + cache: false, + fix: true + } + } + ] + }, { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader' }, ... ] } ...
Тестируй, пробуй 🚨
настроитьstylelint
-
Установить
$ yarn add -D stylelint # 强大的现代化 linter,可帮助您避免错误并在样式中强制执行约定。 $ yarn add -D stylelint-config-recommended # Stylelint 的推荐可共享配置 $ yarn add -D postcss-reporter # 在控制台中记录 PostCSS 消息 # $ yarn add -D stylelint-config-standard # Stylelint 的标准可共享配置 # stylelint 插件通过 PostCSS 注册警告 。因此,您需要用于打印警告的 PostCSS 运行器或插件,其目的是格式化和打印警告(例如 postcss-reporter)
-
Создайте новый файл конфигурации stylelint
$ touch .stylelintrc # stylelint 配置文件
<!-- starter/.eslintrc --> # 你也可以使用 stylint 推荐开启的规则, 只需引入扩展推荐包即可。 # 你也可以 使用 rules 扩充规则或者覆盖推荐规则,这取决于你! { "extends": "stylelint-config-recommended", "rules": { "indentation": 2, // 缩进 "declaration-colon-space-after": "always", // 在冒号声明后需要一个空格或禁止使用空格。 a { color:pink } => a { color: pink } "declaration-colon-space-before": "never", // 在冒号之前需要一个空格或禁止空格。 a { color : pink } => a { color: pink } "function-comma-space-after": "always", // 在功能的逗号后面需要一个空格或不允许空格。 a { transform: translate(1,1) } => a { transform: translate(1, 1) } "function-url-quotes": "always", // 要求或禁止使用网址引号 a { background: url(x.jpg) } => a { background: url("x.jpg") } "media-feature-colon-space-before": "never", // 媒体功能中的冒号之前需要单个空格或不允许使用空格。@media (max-width :600px) {} => @media (max-width:600px) {} "media-feature-name-no-vendor-prefix": true, // 禁止使用媒体功能名称的供应商前缀。@media (-webkit-min-device-pixel-ratio: 1) {} => @media (min-resolution: 96dpi) {} "max-empty-lines": 5, // 限制相邻的空行数。 "number-leading-zero": "never", // 小数部分小于或等于1的前导零。a { line-height: 0.5; } => a { line-height: .5; } "number-no-trailing-zeros": true, // 禁止数字尾随零。a { top: 1.0px } => a { top: 1px } "at-rule-semicolon-newline-after": "always", // 规则后的分号换行符 @import url("x.css"); a {} => @import url("x.css");\n a {} "selector-list-comma-space-before": "never", // 选择器列表的逗号前需要一个空格或不允许空格 a ,b { color: pink; } => a, b { color: pink; } "selector-list-comma-newline-after": "always", // 选择器列表的逗号后需要换行符或不允许使用空格。a, b { color: pink; } => a,\n b { color: pink; } "string-quotes": "single", // 在字符串周围指定单引号或双引号。 a { content: “x”; } => a { content: 'x'; } } }
-
Расширенная общая конфигурация и таблица правил
-
Добавить команды быстрого доступа
<!-- starter/package.json --> "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", "build": "cross-env NODE_ENV=production webpack --color --progress", "lint:script": "eslint --ext '.js,.jsx' src", "lint-fix:script": "npm run lint:script -- --fix", + "lint:style": "stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss", + "lint-fix:style": " npm run lint:style -- --fix", },
-
Настроить postcss-репортер
Журнал сообщений PostCSS в консоли
<!-- starter/postcss.config.js --> module.exports = { plugins: { autoprefixer: {}, + 'postcss-reporter': { + clearReportedMessages: true, # 插件将在记录结果消息后清除它们。这样可以防止其他插件或您使用的任何运行程序再次记录相同的信息并引起混乱。 + throwError: true # 在插件记录您的消息后,如果发现任何警告,它将引发错误。 + }, } };
-
Выполните команду, чтобы увидеть, есть ли какие-либо нарушения
$ yarn lint:style # 格式化 style $ yarn lint-fix:style # 格式化 style 并自动修复 # 结果 $ npm run lint:style -- --fix > starter@1.0.0 lint:style /Users/gt/LEMON/starter > stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss "--fix" src/style/reset.css 54:1 ✖ Expected selector "h1" to come before selector "h1:first-child" no-descending-specificity 54:1 ✖ Expected selector "h1" to come before selector "h1:last-child" no-descending-specificity 58:1 ✖ Expected selector "h2" to come before selector "h2:first-child" no-descending-specificity 58:1 ✖ Expected selector "h2" to come before selector "h2:last-child" no-descending-specificity 62:1 ✖ Expected selector "h3" to come before selector "h3:first-child" no-descending-specificity 62:1 ✖ Expected selector "h3" to come before selector "h3:last-child" no-descending-specificity 66:1 ✖ Expected selector "h4" to come before selector "h4:first-child" no-descending-specificity 66:1 ✖ Expected selector "h4" to come before selector "h4:last-child" no-descending-specificity 67:1 ✖ Expected selector "h5" to come before selector "h5:first-child" no-descending-specificity 67:1 ✖ Expected selector "h5" to come before selector "h5:last-child" no-descending-specificity 68:1 ✖ Expected selector "h6" to come before selector "h6:first-child" no-descending-specificity 68:1 ✖ Expected selector "h6" to come before selector "h6:last-child" no-descending-specificity # no-descending-specificity 禁止较低特异性的选择器在覆盖较高特异性的选择器之后出现。 # 根据规则表修复 reset.css 文件 # 再次运行,结果: $ npm run lint:style -- --fix > starter@1.0.0 lint:style /Users/gt/LEMON/starter > stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss "--fix" ✨ Done in 1.96s.
Протестируй, попробуй 💄
настроитьprettier
-
Установить
$ yarn add -D prettier $ yarn add -D eslint-plugin-prettier # 将 Prettier 作为 ESLint 规则运行,并将差异报告为单个ESLint问题 $ yarn add -D eslint-config-prettier # 关闭所有不必要的或可能与 Prettier 冲突的规则。 $ yarn add -D stylelint-config-prettier # 禁用与 Prettier 冲突的规则的配置
Эти правила отключения см.eslint-config-prettier#special-rules, stylelint-config-prettier special-rules
-
Расширить красивее в конфигурации eslint
<!-- starter/.eslintrc --> { ... - "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:jsx-a11y/recommended"], + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:jsx-a11y/recommended", + "plugin:prettier/recommended", + "prettier/react" + ], ... } <!-- 说明 --> "plugin:prettier/recommended" does three things: 1. Enables eslint-plugin-prettier. 2. Sets the prettier/prettier rule to "error". 3. Extends the eslint-config-prettier configuration. "prettier/react" 为了支持特殊的 ESLint 插件(eslint-plugin-react)所添加额外的排除项
Конечно вы можете
.prettierrc
установить в файлPrettier
собственные варианты. -
Создайте новый более красивый файл конфигурации
$ touch .prettierrc # prettier 配置文件
<!-- starter/.prettierrc --> { "semi": true, "singleQuote": true, "trailingComma": 'all', }
-
Расширить красивее в конфигурации stylelint
<!-- starter/.stylelintrc --> { ... - "extends": "stylelint-config-recommended", + "extends": [ + "stylelint-config-recommended", + "stylelint-config-prettier" + ], ... }
-
иллюстрировать
- Выше мы расширяем конфигурации eslint и stylelint, чтобы интегрировать инструменты и интегрировать их вместе. Итак, вы видите отключение любых существующих правил форматирования в другом линтере, которые могут конфликтовать с тем, как Prettier хочет форматировать код.
-
Добавить ярлык командной строки
<!-- starter/package.json --> ... "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", "build": "cross-env NODE_ENV=production webpack --color --progress", "lint:script": "eslint --ext '.js,.jsx' src", "lint-fix:script": "npm run lint:script -- --fix", "lint:style": "stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss", "lint-fix:style": " npm run lint:style -- --fix", + "prettier": "prettier --check --write 'src/**/*.{js,jsx,scss,css}' --config ./.prettierrc" }, ...
Дополнительные параметры см.Prettier CLI
-
Запустите команду, отформатируйте код
$ yarn prettier # 结果, 它帮你格式化的代码如下 $ prettier --check --write './src/**/*.js' './src/**/*.jsx' Checking formatting... src/index.js src/router/index.js src/router/list.js src/views/Github/Github.js src/views/Setting/Setting.js Code style issues fixed in the above file(s). ✨ Done in 0.79s.
настроитьHusky
Сценарии Git hook полезны для выявления простых проблем перед отправкой на проверку кода. Мы запускаем хуки при каждом коммите, чтобы автоматически указывать на проблемы в коде, такие как отсутствующие точки с запятой, конечные пробелы и операторы отладки. Указав на эти проблемы перед просмотром кода, можно гарантировать, что никакие ошибки не попадут в репозиторий, и, во-вторых, рецензенты кода смогут сосредоточиться на измененной архитектуре, а не тратить время на тривиальные проблемы стиля.
-
Установить
$ yarn add -D husky # 🐶 Git hooks made easy $ yarn add -D lint-staged # 对暂存的 git 文件运行 linters,不要让💩进入您的代码库!
-
настроить
<!-- starter/package.json --> { "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", "build": "cross-env NODE_ENV=production webpack --color --progress", + "lint": "npm run lint:style && npm run lint:script", + "lint-fix": "npm run lint-fix:style && npm run lint-fix:script", "lint:script": "eslint --ext '.js,.jsx' src", "lint-fix:script": "npm run lint:script -- --fix", "lint:style": "stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss", "lint-fix:style": " npm run lint:style -- --fix", "prettier": "prettier --check --write 'src/**/*.{js,jsx,scss,css}' --config ./.prettierrc" }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js, jsx, css, scss}": [ + "npm run prettier", + "npm run lint-fix", + "git add" + ] + } }
Нажмите код, чтобы проверить его! Попытайся!🎊🎊
-
Не по теме: спецификация журнала изменений коммита
# feat: 添加新功能(feature) # fix : 修复 bug # docs: 文档(documentation) # style: 样式及代码格式化等不涉及逻辑的改动点 # refactor: 重构 # test: 添加测试用例 # chore: 构建过程或辅助工具的变动 # 这里推荐一个 lint 插件 commitlint。可根据需要添加 # 详细参考:https://github.com/conventional-changelog/commitlint # 关于 commit 信息编写的更多规范指南 # 请参考:http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html
На этом этапе содержание спецификации кодирования в основном сформулировано, и количество вещей, которые можно сказать, ограничено.Как его настроить, зависит от вас или требований вашей команды! Иди 🚠
20. Улучшить приложение
Для лучшего обсуждения давайте выполним небольшое требование.
-
Преобразование нашего проекта в соответствии с потребностями эскиза
-
Изменить таблицу маршрутизации
import React from 'react'; import loadable from '@loadable/component'; import Loading from '@/components/Loading/Loading'; const BottomTabNavigator = import( /* webpackChunkName: "bottom-tab-navigator" */ '@/components/BottomTabNavigator/BottomTabNavigator' ); const Empty = import( /* webpackChunkName: 'not-found' */ '@/components/Empty/Empty' ); const Github = import(/* webpackChunkName: "github" */ '@/views/Github/Github'); const Setting = import( /* webpackChunkName: "setting" */ '@/views/Setting/Setting' ); const AsyncComponent = loader => loadable(loader, { fallback: <Loading /> }); const routes = [ { path: '/', exact: true, redirect: '/dashboard/github', }, { path: '/dashboard', component: AsyncComponent(() => BottomTabNavigator), routes: [ { path: '/dashboard/github', component: AsyncComponent(() => Github), }, { path: '/dashboard/setting', component: AsyncComponent(() => Setting), }, ], }, { path: '*', component: AsyncComponent(() => Empty), }, ]; export default routes;
-
Страница модернизации Github
/* * 路径: starter/src/views/Github * 说明: * RepositoriesCard 根据草图编写的仓库信息卡片 * Loading 加载态组件 * Empty 空数据态组件 * useRequest 自定义 hook,用于包装请求 * searchRepositories 统一 API 请求封装 * * 提示: 说明涉及到的组件,可以参考项目;你也可以自己实现,这不重要。 */ import React from 'react'; import styles from './Github.scss'; import RepositoriesCard from '@/components/RepositoriesCard/RepositoriesCard'; import Loading from '@components/Loading/Loading'; import Empty from '@components/Empty/Empty'; import useRequest from '@/containers/useRequest'; import { searchRepositories } from '@/services/api/github'; function Github() { const [loading, data] = useRequest(searchRepositories, { q: 'javascript' }); if (loading === true) { return <Loading /> } return ( <div className={styles.root}> {(data && data.items.map( ({ description, id, name, forks_count, stargazers_count, language, owner }) => ( <RepositoriesCard key={id} name={name} avatarUrl={owner.avatar_url} description={description} stargazersCount={stargazers_count} forksCount={forks_count} language={language} /> ) )) || <Empty />} </div> ); } export default Github;
-
Измените страницу настроек (не изменяйте 😜)
Мы преобразуем страницу Github, вызывая метод запроса внутри компонента и единообразно инкапсулируя запрос.Прежде чем продолжить работу по преобразованию, давайте посмотримИнтерфейсные и внутренние взаимодействия
-
21. Интерфейсные и внутренние взаимодействияAxios
-
Установить
$ yarn add axios # Promise based HTTP client for the browser and node.js
-
Новые связанные файлы
# 新建 services 文件夹 $ cd src && mkdir services $ cd services && touch index.js # 基于 axios 简单封装 $ mkdir interface && cd interface # 用于存在项目所有接口 $ touch github.js # 用于存放 GitHub 相关请求
-
Простой пакет src/services/index.js на основе axios
/** * 说明: AXIOS_DEFAULT_OPTIONS 默认配置,详细参考 utils * * 注: 以下封装仅仅简单包装一层,你也可以自己实现。 */ import axios from 'axios'; import constants from '@/utils/constants'; // 使用自定义配置新建一个 axios 实例 const instance = axios.create(constants.AXIOS_DEFAULT_OPTIONS); // 请求拦截器 instance.interceptors.request.use( (AxiosRequsetConfig) => AxiosRequsetConfig, // 在发送请求之前做些什么 (error) => Promise.reject(error) // 对请求错误做些什么 ); // 响应拦截器 instance.interceptors.response.use( (AxiosResponse) => AxiosResponse, // 对响应数据做点什么 (error) => Promise.reject(error) // 对响应错误做点什么, 如,处理一些鉴权类问题 ); export default function (options = {}, customConfig = {}) { return new Promise((resolve, reject) => { const finalConfig = Object.assign(options, customConfig); instance(finalConfig) .then(({ data }) => { if (data) { return resolve(data); } return reject(new Error('Request return result exception!')); }) .catch((reason) => reject(reason)); }); }
-
Слой бизнес-интерфейса src/services/interface/github.js
import network from '../index'; /** * @desc 搜索仓库 * * @param {Object} data 请求参数 * @returns {Promise} */ export const searchRepositories = (data = {}) => network({ url: '/search/repositories', params: data });
Вышеупомянутая простая инкапсуляция основного метода запроса, разделения интерфейсов и т. д. в основном предназначена для помощи в обсуждении.Конечно, это все еще очень просто.Вы можете сделать более полную инкапсуляцию в соответствии с вашими реальными потребностями!
22. Модернизация проекта - Компоненты
UI Component
-
Методические рекомендации
- Самая основная форма компонентов, таких как: кнопки, метки.
- нет статуса
- Чистый статический дисплей
- Базовая структура композиции (реквизит+рендер)
- Нет зависимости от жизненного цикла
- Одинарная нагрузка, мультиплексирование.
-
Образец
import React from 'react'; import PropTypes from 'prop-types'; const UI = ({ title }) => { return ( <div className="UI"> { title } </div> ); }; UI.propTypes = { title: PropTypes.string, }; UI.defaultProps = { title: 'UI Component !', }; export default UI;
Container Component
-
Методические рекомендации
- Принцип единой ответственности для уменьшения связанности компонентов
- Предоставьте данные (например, включите логику обработки возвращаемых данных Ajax)
- Взаимодействовать с инструментами управления состоянием (например, включать логику внедрения Redux)
- состояние
- Меньше стилей и DOM
-
Образец
import { connect } from 'react-redux'; import Demo from 'components/Demo/Demo'; import { incrementEnthusiasm, decrementEnthusiasm } from 'actions/index'; export function mapStateToProps({ enthusiasm }) { return { enthusiasm, }; } export function mapDispatchToProps(dispatch) { return { onIncrement: () => dispatch(actions.incrementEnthusiasm()), onDecrement: () => dispatch(actions.decrementEnthusiasm()), }; } export default connect(mapStateToProps, mapDispatchToProps)(Demo);
Tip: Поскольку я не очень хорошо знаком с реакцией, говорить об этом относительно просто.Вот рекомендуемая ссылка:Presentational and Container Components,Напишите устойчивые компоненты
23. Трансформация проекта - Адаптация мобильного терминала
Здесь мы непосредственно вводим
postcss-px-to-viewport
плагин.
-
Установить
$ yarn add -D postcss-px-to-viewport`
-
настроить
<!-- starter/postcss-config.js --> module.exports = { plugins: { autoprefixer: {} }, 'postcss-reporter': { clearReportedMessages: true, throwError: true }, + 'postcss-px-to-viewport': { + viewportWidth: 375, // 设计稿的视口宽度 + viewportHeight: 812, // 设计稿的视口高度 + unitPrecision: 5, // 单位转换后保留的精度 + viewportUnit: 'vw', // 希望使用的视口单位 + fontViewportUnit: 'vw', // 字体使用的视口单位 + selectorBlackList: ['.ignore', '.hairlines'], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。 + minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换 + mediaQuery: false, // 媒体查询里的单位是否需要转换单位 + exclude: [/node_modules/] // 需要排除的 + } };
-
Запустите проект и увидите эффект!
$ yarn server # 运行项目 # 结果 $ cross-env NODE_ENV=development webpack-dev-server --color --progress 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://localhost:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully.
🔥 Хорошая работа! 🎉 🔥
Посмотреть полный каталог проектов на данном этапе
└── starter ├── dist │ ├── chunks │ │ ├── bottom-tab-navigator.77d17027.js │ │ ├── github.4e7f6c35.js │ │ ├── not-found.638dbdfc.js │ │ ├── setting.bc3fbe14.js │ │ ├── vendors~github.7acdaa67.js │ │ ├── vendors~github.7acdaa67.js.LICENSE │ │ ├── vendors~main.b1a4bdbf.js │ │ └── vendors~main.b1a4bdbf.js.LICENSE │ ├── css │ │ ├── bottom-tab-navigator.25c0dead.css │ │ ├── github.866c72ba.css │ │ ├── main.45091b7c.css │ │ ├── not-found.d566b1be.css │ │ └── setting.2b60ef7c.css │ ├── fonts │ │ ├── iconfont.63765329.woff │ │ ├── iconfont.c2eabadd.ttf │ │ └── iconfont.cad7bb52.eot │ ├── images │ │ ├── empty-data.788c1924.png │ │ ├── logo.581fa1d8.png │ │ └── webpage-lost.a02f7942.png │ ├── svg │ │ └── iconfont.1247822e.svg │ ├── main.a010b425.js │ └── index.html ├── node_modules | ├── ... | └── ... ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ ├── font │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ └── iconfont.woff │ │ └── images │ │ ├── empty-data.png │ │ ├── logo.png │ │ └── webpage-lost.png │ ├── components │ │ ├── BottomTabNavigator │ │ │ ├── BottomTabNavigator.js │ │ │ ├── BottomTabNavigator.scss │ │ │ └── index.zh-CN.md │ │ ├── Circle │ │ │ ├── Circle.js │ │ │ ├── Circle.scss │ │ │ └── index.zh-CN.md │ │ ├── Empty │ │ │ ├── Empty.js │ │ │ ├── Empty.scss │ │ │ └── index.zh-CN.md │ │ ├── Loading │ │ │ ├── Loading.js │ │ │ ├── Loading.scss │ │ │ └── index.zh-CN.md │ │ ├── README.md │ │ └── RepositoriesCard │ │ ├── RepositoriesCard.js │ │ ├── RepositoriesCard.scss │ │ └── index.zh-CN.md │ ├── containers │ │ ├── README.md │ │ └── useRequest.js │ ├── index.js │ ├── router │ │ ├── index.js │ │ └── list.js │ ├── services │ │ ├── index.js │ │ └── interface │ │ └── github.js │ ├── style │ │ ├── global.css │ │ ├── reset.css │ │ └── variable.scss │ ├── utils │ │ ├── constants.js │ │ ├── enume.js │ │ └── tools.js │ └── views │ ├── Github │ │ ├── Github.js │ │ └── Github.scss │ └── Setting │ ├── Setting.js │ └── Setting.scss ├── webpack.config.js ├── postcss.config.js ├── package.json ├── LICENSE ├── README.md └── yarn.lock
Реновация проекта в основном завершена, но в будущем еще есть над чем работать 💊😯. Продолжать!
24. Разделяйте макеты спереди назад
Фронтенд и бэкенд разделены, что позволяет разрабатывать фронтенд независимо от бэкенда. Большую роль играет мокап. В реальном развитии бизнеса нам нужен метод, который может перехватывать запросы и возвращать смоделированные данные без вторжения в существующий код. мы используемjson-serverПомогите нам удовлетворить эту потребность.
-
Установить
$ yarn add -D json-server
-
Создайте новую фиктивную папку
$ mkdir mock $ cd mock && touch index.js $ mkdir interface && cd interface $ touch index.js && touch github.js
// starter/mock/index.js const data = require('./interface/index'); module.exports = function Mock() { return data; }; // starter/mock/interface/index.js const github = require('./github'); module.exports = { ...github, }; // starter/mock/interface/github.js const repositories = { "items": [ { "id": 6498492, "name": "javascript", "full_name": "airbnb/javascript", "owner": { "login": "airbnb", "id": 698437, "avatar_url": "https://avatars3.githubusercontent.com/u/698437?v=4", }, "description": "JavaScript Style Guide", "size": 3002, "stargazers_count": 89966, "watchers_count": 89966, "language": "JavaScript", "forks_count": 17404, "open_issues_count": 110, "license": { "key": "mit", "name": "MIT License", }, "forks": 17404, "open_issues": 110, "watchers": 89966, "default_branch": "master", "score": 151.055 }, { "id": 18286232, "name": "javascript", "full_name": "GitbookIO/javascript", "private": false, "owner": { "login": "GitbookIO", "id": 7111340, "avatar_url": "https://avatars0.githubusercontent.com/u/7111340?v=4", }, "description": "GitBook teaching programming basics with Javascript", "size": 1267, "stargazers_count": 1923, "watchers_count": 1923, "language": "javascript", "forks_count": 730, "open_issues_count": 43, "license": { "key": "apache-2.0", "name": "Apache License 2.0", }, "forks": 730, "open_issues": 43, "watchers": 1923, "default_branch": "master", "score": 104.4313 }, ] } module.exports = { repositories };
Данные поступают с GitHub, который предназначен только для демонстрации, поэтому данные публикуются напрямую. Если вам нужно динамически генерировать данные, вы можете ввестиmockjsпомочь вам сгенерировать данные. Я не буду вдаваться в подробности здесь!
-
Настроить ярлык команды start/package.json
... { "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", + "server:mock": "npm run mock & cross-env NODE_ENV=development MOCK=true webpack-dev-server --color --progress", + "mock": "json-server mock/index.js --watch --port 3001", "build": "cross-env NODE_ENV=production webpack --color --progress", "lint": "npm run lint:style && npm run lint:script", "lint-fix": "npm run lint-fix:style && npm run lint-fix:script", "lint:script": "eslint --ext '.js,.jsx' src", "lint-fix:script": "npm run lint:script -- --fix", "lint:style": "stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss", "lint-fix:style": " npm run lint:style -- --fix", "prettier": "prettier --check --write 'src/**/*.{js,jsx,scss,css}' --config ./.prettierrc" }, } ...
-
Настройте агент dev-сервера
# 新建代理文件 $ cd ../.. && mkdir config $ cd config && touch proxy.js
// starter/config/proxy.js /** * @desc mock 服务代理配置 */ const MOCK_SERVER_PROXY = { '/search/*': { target: 'http://localhost:3001/$1', } } /** * @desc 默认服务代理 */ const DEFAULT_PROXY = {}; /** * @desc dev-server 代理配置 * @param {Boolean} IS_MOCK mock 标识 * @param {Object} Proxy */ module.exports = function({ IS_MOCK }) { if (IS_MOCK) return MOCK_SERVER_PROXY; return DEFAULT_PROXY; }
Конкретно как настроить прокси, по кастомному интерфейсу! Для получения дополнительной информации см.
devServer - proxy
# starter/webpack.config.js ... + const IS_MOCK = process.env.MOCK === 'true'; + const filterProxy = require('./config/proxy'); ... baseConfig.devServer = { ... + proxy: filterProxy({ IS_MOCK }) } ...
-
запустить проект
$ yarn server:mock # 结果 $ npm run mock & cross-env NODE_ENV=development MOCK=true webpack-dev-server --color --progress > starter@1.0.0 mock /Users/mr.lemon/cl/CODE_CL/REACT/starter > json-server mock/index.js --watch --port 3001 \{^_^}/ hi! Loading mock/index.js Done Resources http://localhost:3001/repositories Home http://localhost:3001 Type s + enter at any time to create a snapshot of the database Watching... 10% building 1/1 modules 0 activeℹ 「wds」: Project is running at http://192.168.0.102:3000/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/mr.lemon/cl/CODE_CL/REACT/starter/public ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Compiled successfully.
Выше описана только фиктивная часть, а что касается разделения фронтэнда и бэкенда, вот вопрос и ответ.Имеет ли смысл разделять переднюю и заднюю части Интернета?
25. Модульное тестированиеjest
Модульное тестирование — это работа по тестированию модуля, функции или класса для проверки правильности. В отрасли существует множество отличных фреймворков для тестирования, так что выбирайте прямо здесь.jest.
-
Установить
$ yarn add -D jest # Jest is a delightful JavaScript Testing Framework with a focus on simplicity. $ yarn add -D babel-jest # Jest plugin to use babel for transformation $ yarn add -D enzyme # 一种用于 React 的 JavaScript 测试实用程序,可以更轻松地测试 React 组件的输出。您还可以操纵,遍历并以某种方式模拟给定输出的运行时。 $ yarn add -D enzyme-adapter-react-16 # react 16 适配器 $ yarn add -D identity-obj-proxy # 模拟一个代理以启用 className 查找
-
Создайте новую папку для хранения тестовых случаев и файла конфигурации jest.
$ touch jest.config.js $ cd src && mkdir __tests__ $ cd __tests__ $ mkdir __mocks__ && mkdir ui && touch setup.js $ cd __mocks__ && touch fileMock.js $ cd ../ui && touch Loading.spec.js
-
настроить шутку
<!-- starter/jest.config.js --> module.exports = { testRegex: '(\\.)(test|spec)(\\.)jsx?$', // 处理静态文件 // 样式表和图像等,这些文件在测试中无足轻重,因为我们可以安全地 mock 他们。 // 模拟 CSS 模块,用类名查找模拟一个代理 moduleNameMapper: { '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/src/__tests__/__mocks__/fileMock.js', '\\.(css|scss|sass)$': 'identity-obj-proxy', '^@/(.*)$': '<rootDir>/src/$1' }, // 为转换源文件提供同步功能的模块 transform: { '^.+\\.(js|jsx)$': 'babel-jest' }, // 在每次测试之前配置或设置测试环境 setupFiles: ['<rootDir>/src/__tests__/setupTests.js'] }; <!-- starter/src/__tests__/__mocks__/fileMock.js --> module.exports = 'test-file-stub';
-
Зарегистрируйте конфигурацию адаптера фермента
// starter/src/__tests__/setup.js import enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; enzyme.configure({ adapter: new Adapter() });
-
Настройте команды быстрого запуска
<!-- starter/package.json --> { ... "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "jest --config jest.config.js --no-cache", "server": "cross-env NODE_ENV=development webpack-dev-server --color --progress", "server:mock": "npm run mock & cross-env NODE_ENV=development MOCK=true webpack-dev-server --color --progress", "mock": "json-server mock/index.js --watch --port 3001", "build": "cross-env NODE_ENV=production webpack --color --progress", "lint": "npm run lint:style && npm run lint:script", "lint-fix": "npm run lint-fix:style && npm run lint-fix:script", "lint:script": "eslint --ext '.js,.jsx' src", "lint-fix:script": "npm run lint:script -- --fix", "lint:style": "stylelint 'src/**/*.css' 'src/**/*.scss' --syntax scss", "lint-fix:style": " npm run lint:style -- --fix", "prettier": "prettier --check --write 'src/**/*.{js,jsx,scss,css}' --config ./.prettierrc" }, ... }
-
Пишите тестовые случаи
import React from 'react'; import { shallow } from 'enzyme'; import Loading from '../../components/Loading/Loading'; describe('Loading 组件基础测试组合!', () => { it('<Loading /> 组件默认标题应该是 "loading..."', () => { const loading = shallow(<Loading />); expect(loading.find('span').text()).toBe('loading...'); }); it('<Loading /> 组件标题应该是 "加载中..."', () => { const loading = shallow(<Loading title='加载中...' />); expect(loading.find('span').text()).toBe('加载中...'); }); });
Варианты использования здесь только для демонстрации, в реальной разработке варианты использования должны быть написаны строго в соответствии с функциями компонентов пользовательского интерфейса.
-
запустить тест
$ yarn test # 结果 $ jest --config jest.config.js --no-cache PASS src/__tests__/ui/Loading.spec.js Loading 组件基础测试组合! ✓ <Loading /> 组件默认标题应该是 "loading..." (7ms) ✓ <Loading /> 组件标题应该是 "加载中..." (1ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 1.66s Ran all test suites. ✨ Done in 2.44s.
-
иллюстрировать
- Написание тестовых случаев важно! Выше обсуждается только то, как получить доступ к шутке и написать ее в соответствии с реальными потребностями.
- Рекомендуется сосредоточиться на приватных функциях инструмента и компонентах UI, про бизнес писать не рекомендуется из-за слишком большой вариативности!
- Для тестовых случаев вы можете обратиться к некоторым библиотекам компонентов пользовательского интерфейса в отрасли, например:element-UI,antd
- Рекомендовать статьюПрактика фронтенд-тестирования
try it!🍁
26. Развертывание запущено
Пакет ресурсов для анализа
В реальных проектах, принимая во внимание производительность интерфейса (загрузка первого экрана, полноэкранная загрузка и время белого экрана), будет проанализирован набранный пакет ресурсов, а затем будут приняты соответствующие решения для оптимизации. Когда мы обсуждали упаковку и конструкцию ранее, мы уже объяснили это во многих аспектах, поэтому я не буду повторяться здесь.
-
# 安装 $ yarn add -D webpack-bundle-analyzer # 交互式可缩放树图可视化webpack输出文件的大小。
# 添加相应配置 starter/webpack.config.js ... const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); if (IS_PROD) { ... + baseConfig.plugins.push( + new BundleAnalyzerPlugin() + ); } ...
$ yarn build # 结果 $ cross-env NODE_ENV=production webpack --color --progress 98% after emitting SizeLimitsPluginWebpack Bundle Analyzer is started at http://127.0.0.1:8888 Use Ctrl+C to close it Hash: 3bf5742858f4d53ed50d Version: webpack 4.41.2 Time: 3079ms Built at: 2019-10-27 21:43:54 Asset Size Chunks Chunk Names chunks/bottom-tab-navigator.04852ffb.js 1.54 KiB 0 [emitted] [immutable] bottom-tab-navigator chunks/github.a1f67f6e.js 4.65 KiB 1, 3 [emitted] [immutable] github chunks/not-found.b1d86988.js 576 bytes 3 [emitted] [immutable] not-found chunks/setting.34d43c5a.js 673 bytes 4 [emitted] [immutable] setting chunks/vendors~github.d521d263.js 24.5 KiB 5 [emitted] [immutable] vendors~github chunks/vendors~github.d521d263.js.LICENSE 120 bytes [emitted] chunks/vendors~main.89c9b5d2.js 166 KiB 6 [emitted] [immutable] vendors~main chunks/vendors~main.89c9b5d2.js.LICENSE 1.01 KiB [emitted] css/bottom-tab-navigator.b9b2f600.css 1.54 KiB 0 [emitted] [immutable] bottom-tab-navigator css/github.e0893952.css 1.99 KiB 1, 3 [emitted] [immutable] github css/main.09cf98ab.css 11 KiB 2 [emitted] [immutable] main css/not-found.091bbea2.css 64 bytes 3 [emitted] [immutable] not-found css/setting.42e9f58f.css 252 bytes 4 [emitted] [immutable] setting fonts/iconfont.63765329.woff 4.58 KiB [emitted] fonts/iconfont.c2eabadd.ttf 7.52 KiB [emitted] fonts/iconfont.cad7bb52.eot 7.69 KiB [emitted] images/empty-data.788c1924.png 11.7 KiB [emitted] images/logo.581fa1d8.png 8.38 KiB [emitted] images/webpage-lost.a02f7942.png 13.5 KiB [emitted] index.html 667 bytes [emitted] main.02466eca.js 6.64 KiB 2 [emitted] [immutable] main svg/iconfont.1247822e.svg 22 KiB [emitted] Entrypoint main = chunks/vendors~main.89c9b5d2.js css/main.09cf98ab.css main.02466eca.js
Travis CI
-
В реальной среде разработки внутри компании будет набор элементов непрерывной интеграции, которые помогут нам в развертывании. Здесь для удобства демонстрации мы используемTravis CIПомогите нам сделать это.
-
Travis CI: вносите изменения в код, автоматически запускайте сборки и тесты и сообщайте о результатах.
-
Как использовать конфигурацию? Пожалуйста, обратитесь кУчебное пособие Travis CI по службе непрерывной интеграции
-
yml-скрипт этого проекта см.проект
-
предварительный просмотр"предварительный просмотр"
Эпилог
- На этом этапе обсуждается весь процесс создания веб-приложения!
- Вся статья больше похожа на список и частичное резюме. Если я смогу достичь этого шаг за шагом, я верю, что это определенно что-то принесет!
- Я так много написал...
- Наконец, если есть ошибка в тексте, спасибо указать!
- 【адрес проекта】
видеть
- git
- gitignore
- node.js
- npm
- yarn
- editorconfig
- browserslist
- webpack
- React
- Babel
- @babel/core
- @babel/preset-env
- @babel/preset-react
- @babel/plugin-transform-runtime
- babel-loader
- webpack-dev-server
- cross-env
- webpack mode
- HotModuleReplacementPlugin
- react-hot-loader
- @hot-loader/react-dom
- Sass
- node-sass
- sass-loader
- css-loader
- style-loader
- peerDependencies
- autoprefixer
- css-modules
- postcss-loader
- postcss
- react-router-dom
- html-webpack-plugin
- mini-css-extract-plugin
- clean-webpack-plugin
- optimization.splitChunks
- @babel/plugin-transform-runtime
- webpack.DefinePlugin
- uglifyjs-webpack-plugin
- UglifyJS2/issues/659
- Почему webpack4 по умолчанию поддерживает сжатие синтаксиса ES6?
- terser-webpack-plugin
- terser
- optimize-css-assets-webpack-plugin
- postcss-safe-parser
- cssnano
- externals
- cdnjs
- jsdelivr
- Что такое CDN? Каковы преимущества использования CDN?
- Сеть доставки контента
- UNPKG
- react-router
- Управление ресурсами
- Разрешение модуля
- url-loader
- @loadable/component
- eslint
- stylelint
- prettier
- eslint-loader
- babel-eslint
- airbnb/javascript
- eslint-plugin-react
- eslint-plugin-react-hooks
- eslint-plugin-jsx-a11y
- eslint-plugin-import
- eslint-import-resolver-webpack
- stylelint
- stylelint-config-recommended
- stylelint-config-standard
- postcss-reporter
- prettier
- eslint-plugin-prettier
- eslint-config-prettier
- Prettier CLI
- stylelint-config-prettier
- pre-commit
- lint-staged
- Husky
- Сообщение фиксации и руководство по написанию журнала изменений
- commitlint
- Axios
- Presentational and Container Components
- Напишите устойчивые компоненты
- postcss-px-to-viewport
- json-server
- mockjs
- jest
- babel-jest
- enzyme
- enzyme-adapter-react-16
- webpack-bundle-analyzer
- Учебное пособие Travis CI по службе непрерывной интеграции
- Travis CI