新的知识仓库 前端从入门到入土 求关注、star及提建议,不定时更新~
你们都不看的总集篇: 从零开始的大前端筑基之旅(深入浅出,持续更新~)
觉得不错就顺手点个赞吧~
写前端一年多了,用的都是大佬建好的架子,还没自己从头建个项目,现在开始踩坑。
项目大体包括以下几部分
- 目录规范
- react
- webpack
- less
- typescript
- eslint
- axios封装
初始化项目
首先,新建个文件夹basic-react-app
。名字不是重点,你高兴就好。
然后,使用 npm init
初始化项目,依据提示,一路回车下去
$basic-react-app npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
// 从这里开始,使用默认的话回车即可
package name: (basic-react-app)
version: (1.0.0)
description: basic react demo
entry point: (index.js)
test command:
git repository:
keywords:
author: suil
license: (ISC)
About to write to /Users/zhangpengcheng15/Documents/code/temp/basic-react-app/package.json:
{
"name": "basic-react-app",
"version": "1.0.0",
"description": "basic react demo",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "suil",
"license": "ISC"
}
Is this OK? (yes)
现在,你得到了一个package.json
文件
然后,使用git init
初始化仓库,可以考虑先在远端初始化仓库,再拉去到本地。
$basic-react-app git init
Initialized empty Git repository in /Users/zhangpengcheng15/Documents/code/temp/basic-react-app/.git/
$basic-react-app git:(master) ✗
如果你使用了特殊的命令窗或zsh,应该就可以看到目录后面显示git:(master)
,代表本地仓库已经建好了。
别着急下一步,不要忘记配置gitignore
,否则电脑配置不好会卡顿一阵子。
在根目录下新建.gitignore
文件,使用编辑器打开,输入
node_modules
dist
并保存。好了,现在仓库初始化结束。
目录规范
最后,打开你喜欢的编辑器,比如 vscode
,来创建如下文件结构。
.
+ |- /src
+ |- /assets
+ |- /less
+ |- /icons
+ |- /components
+ |- /constants
+ |- /static
+ |- /imgs
+ |- /utils
使用
vscode
的同学,一定要下vscode-icons
插件呀
其中,
- assets 放置资源文件,包括各种类型的icon、全局使用的样式表等
- components 抽取的组件,下属每个文件夹都是一个系列的组件
- constants 常量资源,比如抽取出的配置常量,公共的接口定义、统一放置请求映射等
- static 静态资源,包括多语言资源、图片资源、字体资源、第三方lib资源等
- utils 工具类包,项目中抽取出的共用方法之类的
好了,文件结构建完成。下面开始安装 react
React
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
-
React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。
-
React 并没有采用将标记与逻辑进行分离到不同文件这种人为地分离方式,而是通过将二者共同存放在称之为“组件”的松散耦合单元之中,来实现关注点分离。
使用命令行安装 react
及 react-dom
npm install react react-dom
或者 yarn add react react-dom
在开始下一步之前,我们先在目录中添加一些文件。
在src目录下,新建index.tsx
和 index.less
,先不用纠结能否被识别或者运行的问题,下一步我们再来解决它。
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.less'; // 一定要加 './' 表示当前目录下
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
h1{
color: blue;
}
webpack
webpack 是什么
webpack
是一个现代 JavaScript
应用程序的静态模块打包器,当 webpack
处理应用程序时,会递归构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle
。
webpack 的核心概念
- entry: 入口
- output: 输出
- loader: 模块转换器,用于把模块原内容按照需求转换成新内容
- 插件(plugins): 扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情
在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):
npm install webpack webpack-cli --save-dev
或者
yarn add webpack webpack-cli --dev
当前webpack基于最新的版本
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件。
本文目的在于配置一个可用的
react
项目,因此有些配置会一步到位,如需更多webpack知识,请移步 webpack中文网
在根目录创建一个 webpack.config.js
,写入如下内容
const path = require('path');
module.exports = {
entry: './src/index.tsx',
mode: "development",
devtool: 'cheap-module-eval-source-map',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
如果 webpack.config.js 存在,则 webpack 命令将默认选择使用它。使用 --config 选项可以传递任何名称的配置文件。
对于多入口文件,可以使用如下配置
entry: {
index: './src/index.js',
index2: './src/index2.js',
},
output: {
path: path.resolve(__dirname,'dist'), //此处若非绝对路径,可能报错
filename: '[name].bundle.js',
},
在上面的配置文件中,我们使用了入口 entry
和出口 output
两个概念,相信你也理解这两个配置的含义。后面我们会用到另外两个概念。
mode
mode 配置项,告知 webpack 使用相应模式的内置优化。
mode 支持以下两个配置:
-
development:将 process.env.NODE_ENV 的值设置为 development,启用 NamedChunksPlugin 和 NamedModulesPlugin
-
production:将 process.env.NODE_ENV 的值设置为 production,启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin
简单来说,*开发环境(development)和生产环境(production)*的构建目标差异很大。将 mode
设置为development
会启用一些webpack默认的优化。这里我们先设置为 mode: "development",
devtool
devtool
中的一些设置,可以帮助我们将编译后的代码映射回原始源代码。不同的值会明显影响到构建和重新构建的速度。
对我们而言,能够定位到源码的行即可,因此,综合构建速度,在开发模式下,devtool
的值设置为cheap-module-eval-source-map
。
其他的配置参数可参考devtool,
Babel
Babel 是一个 JavaScript 编译器
ES2015 中的 import 和 export 语句已经被标准化。虽然大多数浏览器还无法支持它们,但是 webpack 却能够提供开箱即用般的支持。
事实上,webpack 在幕后会将代码“转译”,以便旧版本浏览器可以执行。但是注意的是,webpack 不会更改代码中除 import 和 export 语句以外的部分。如果我们需要使用其它 ES2015 特性,需要在 webpack 的 loader 系统中使用了一个像是 Babel 或 Bublé 的转译器。
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
- 源码转换 (codemods)
执行下面的指令安装 babel
系列
yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-react babel-loader --dev
其中,
- @babel/preset-env 是一个智能预置,允许我们使用最新的JavaScript,而不需要管理在目标环境中哪些语法需要转换(以及可选的浏览器填充)。这既使我们的工作更轻松,也使JavaScript包更小!
- @babel/preset-react 能够转换 JSX 语法
webpack 最出色的功能之一就是,除了 JavaScript,还可以通过 loader 引入任何其他类型的文件。也就是说,以上列出的那些 JavaScript 的优点(例如显式依赖),同样可以用来构建网站或 web 应用程序中的所有非 JavaScript 内容。
现在,在 webpack 配置对象中,添加 babel-loader 到 module 的 loaders 列表中,,当前配置文件如下
module.exports = {
entry: './src/index.tsx',
mode: "development",
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
},
],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
我下载了那么多包,你就用了这一个?你专门解释的那两个包呢?
不着急,其他的包我们通过设置babel配置文件来使用。在根目录下新建 .babelrc
文件,输入如下内容
{
"presets": [
"@babel/env",
"@babel/preset-react"
]
}
既然说到这里,我们先还个债,还记得上面新建的 index.tsx
文件么?webpack不认识tsx文件,浏览器也不认识 tsx 文件,那谁来认呢?
当然是babel
了,在7之前的版本中,我们需要专门的 ts-loader
来转义 ts
和tsx
类型的文件,但是,现在只需要有babel
就够了
无论代码是否具有 ES2015 特性,JSX,TypeScript,还是其他疯狂的自定义————编译器都知道要做什么。向 ts-loader、ts-jest、ts-karma、create-react-app-typescript 等等说再见就好啦,使用 Babel 代替它们。
yarn add @babel/preset-typescript --dev
在.babelrc
文件中补充
{
"presets": [
"@babel/env",
"@babel/preset-react",
+ "@babel/preset-typescript"
],
}
好了,到此,ts | tsx 文件可以使用babel-loader编译了,记得修改webpack.config.js
文件,补充 ts | tsx
使用babel-loader
编译
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
},
],
Loader
对于loader的用法,
test
字段是匹配规则,针对符合规则的文件进行处理。
use
字段有几种写法
- 可以是一个字符串,例如上面的
use: 'babel-loader'
- 可以是一个数组,例如下面处理CSS文件时,
use: ['style-loader', 'css-loader']
use
数组的每一项既可以是字符串也可以是一个对象,当我们需要在webpack
的配置文件中对loader
进行配置,就需要将其编写为一个对象,并且在此对象的options
字段中进行配置,例如上面babel-loader
可以使用另一种配置形式
rules: [
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env"]
}
},
exclude: /node_modules/
}
]
css
现在,去还另一个债,less文件。要让webpack识别 less
文件,自然是 less-loader
了。
当然,我们顺手也补充 css
文件的识别。
有关 less 及 sass 的选择,可移步 Sass.vs.Less | 简介与比较
为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中 安装并添加 style-loader 和 css-loader:
yarn add style-loader css-loader less-loader --dev
webpack.config.js
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
},
+ {
+ test: /\.css$/,
+ use: [
+ 'style-loader',
+ 'css-loader'
+ ]
+ },
+ {
+ test: /\.less$/,
+ use: [
+ 'style-loader',
+ 'css-loader',
+ 'less-loader'
+ ]
+ }
],
小提示:vscode中,选中
+
号,然后按win + D
,就可以连续选中加号,最后按删除就可以删除所有的+
号
postcss-loader
PostCss是一个样式处理工具,它通过自定义的插件和工具生态体系来重新定义css。它鼓励开发者使用规范的css原生语法编写代码,然后配置编译器转换需要兼容的浏览器版本,最后通过编译将源码转换为目标浏览器可用的css代码。
yarn add postcss-loader autoprefixer --dev
webpack.config.js
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
+ 'postcss-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
+ 'postcss-loader',
'less-loader'
]
}
],
postcss-loader 是专门用来加浏览器前缀的,但是它自己也就只能加个前缀而已,因为它自己不知道哪个该加,哪个不该加。所以我们需要 autoprefixer 来告诉它,哪个加,哪个不加。
在根目录新增postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
在package.json
中补充下列字段,来确定我们要支持到哪一步
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%"
]
未雨绸缪,我们在此安装另外两个会用到的loader
file-loader 和 url-loader
- 使用 file-loader,我们可以轻松地将图片、字体等内容混合到 打包文件中。
- url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
yarn add url-loader file-loader --dev
在webpack.config.js
中补充rules规则
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 2048,
}
},
'file-loader',
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
resolve
Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。
resolve.alias
创建 import 或 require 的别名,来确保模块引入变得更简单。例如,一些位于 src/ 文件夹下的常用模块:
resolve: {
alias: {
'@': path.resolve(__dirname, "src/")
}
}
如此,引用自己封装的组件或函数就可以以@
作为起始,例如
import BaseButton from "./components/Button/BaseButton";
这样,避免了无止尽的../../../
,而且万一某个组件切换了目录,组件里的外部引用也不需要重新调整,因为它们总是相对于src路径开始寻找。
请注意,此处只是允许
webpack
打包时识别,要开启路径提示,需要配置tsconfig.json
中的path
,文末有附配置代码
Plugin
clean-webpack-plugin
每次文件修改后,重新打包,导致 dist 目录下的文件越来越多怎么办?
快使用clean-webpack-plugin
,每次构建自动为你清理/dist/
目录,留你想留,清你想清,一键配置,呵护懒癌晚期的你
yarn add clean-webpack-plugin --dev
webpack.config.js
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
+ new CleanWebpackPlugin({
+ cleanOnceBeforeBuildPatterns:['**/*', '!store', '!store/**'] // 不删除store目录下的文件
+ }),
],
};
注意:本文没有拆分不同环境的配置文件,在本地调试时,请注释掉这个插件
更多配置移步clean-webpack-plugin
HtmlWebpackPlugin
你可能需要一个HtmlWebpackPlugin
,否则,你可能会想,react组件渲染了挂在哪里呢?找不到的话它会自己造一个Html
模版么?
并不是,我只是因为懒没有创建模版文件,
当没有这个插件的时候,每次webpack打包生成的[name].bundle.js
需要手动配置,现在,它解放了你的双手,你可以快乐的干点别的事了。
yarn add html-webpack-plugin --dev
不论你之前有没有创建,HtmlWebpackPlugin
会默认在输出目录
生成 index.html
文件,所有的 bundle 会自动添加到 html 中。
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns:['**/*', '!store', '!store/**'] // 不删除store目录下的文件
}),
+ new HtmlWebpackPlugin({
+ title: 'Basic react app'
+ })
],
当然,一般来说,我们有时需要在public
目录下新建一个index.html
的模版文件,里面定义了一些我们定制的内容,但我还是不想占用我的双手在里面引入打包生成的bundle.js
文件。
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html', //打包后的文件名
config: config.template
})
更多功能移步html-webpack-plugin
webpack-dev-server
说了这么多,终于到了遇见Hello world
的时候了。否则我担心你会看不下去。
webpack-dev-server 提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。让我们运行以下命令:
yarn add webpack-dev-server --dev
修改配置文件
+ devServer: {
+ port: '3000', //默认是8080
+ publicPath: "http://localhost:3000/dist/",
+ hotOnly: true,
+ contentBase: './dist'
+ },
在package.json中补充脚本
"scripts": {
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
"build": "webpack"
},
在命令行先执行yarn build
,再执行yarn start
,页面自动打开 -> 遇见Hello World
Typescript
eslint
虽然我们使用babel来编译 ts|tsx
文件,但是,有时候我们还会怀念ts的类型错误检查,比如
“不!我不会编译这玩意儿的!你的代码在42个不同的文件中出现异常!”
如何去检查类型错误呢?添加一段 lint 脚本来唤起 TypeScript 编译器。例如,将 npm test 命令调整为先检查类型,然后再继续运行单元测试。
由于性能问题,TypeScript 官方决定全面采用 ESLint,甚至把仓库(Repository)作为测试平台,而 ESLint 的 TypeScript 解析器也成为独立项目,专注解决双方兼容性问题。
yarn add eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev
同时,我们是一个react app 项目,所以,你懂的
yarn add eslint-plugin-react eslint-plugin-react-hooks --dev
在根目录下新建.eslintrc.js
文件,填入如下内容
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 11,
"sourceType": "module",
project: './tsconfig.json',
},
plugins: [
"react",
"react-hooks",
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
],
};
总有些文件是不需要检查的,做人么,可以为难别人,但不能为难自己,所以,在根目录创建一个.eslintignore
文件
config/
scripts/
node_modules/
\.eslintrc.js // 干掉它自己。。。
webpack.config.js
经过考虑,补充 airbnb
规则
yarn add eslint-config-airbnb-typescript eslint-plugin-jsx-a11y eslint-plugin-import --dev
eslintrc.js文件替换为
extends: [
'airbnb-typescript',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
],
不要忘了在
vscode
中下载eslint
插件
虽然上述规则经过了检验,但总有些对于我们自己来说是多余或者缺失的,所以在.eslintrc.js
文件补充 rules字段,用于开启或关闭某些规则
"rules": {
// 禁止使用 var
'no-var': "error",
// 优先使用 interface 而不是 type
'@typescript-eslint/consistent-type-definitions': [
"error",
"interface"
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
}
修改package.json
中script脚本
"scripts": {
"lint": "eslint --ext .ts --ext .tsx src/",
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
"build": "webpack"
},
执行yarn lint
,然后愉快的扇自己吧。。。。
我在
src/index.tsx
中留了三个错误,大概? 你可以试着改一下
受不了检查记得禁止一些规则
custom.d.ts
突然发现我没写怎么生成tsconfig.json
,两个方法,
- 找个现成的复制到根目录,当然,不要乱复制
- 如果全局安装typescript,执行
tsc --init
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"outDir": "./dist/",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
// "isolatedModules": true,
// "noEmit": true,
"jsx": "react",
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*":["src/*"]
}
},
"include": [
"src","test",
]
}
配置项不是本文重点,主要是为了下面的内容。
在typscript中是无法识别非代码资源的,所以如果你试图import
一个svg|png
之类的文件就会提示你 cannot find module '.png'
。
custom.d.ts
declare module "*.svg" {
const content: any;
export default content;
}
declare module "*.png" {
const content: any;
export default content;
}
在tsconfig.json中有一个include字段,里面的内容表示ts需要转义的内容,将custom.d.ts
文件放到include
字段包含的目录中,就可以解决类型不识别的问题。
最后,这里附上
airbnb
规则传送门【airbnb】typescript-eslint
规则传送门【typescript-eslint】- 文章传送门 【论如何优雅的写出不会挨揍的代码】
本篇教程到此结束,这只是一篇简明的指导式教程,如果有什么不对的地方,欢迎在评论中指出,我会及时修改
后续会针对webpack、eslint语法规范专门整理一期,工作较忙,时间就随缘了~
如果你收获了新知识,请点个赞吧~
本文收纳于: 从零开始的大前端筑基之旅(深入浅出,持续更新~)
推荐阅读:
三言两语带你理解「闭包」|附使用场景
很简单就能解释清的东西为什么要多费口舌呢?朝花夕拾,重新介绍继承与原型链
有图有真相的讲解回流(reflow)与重绘(repaint),KFC与MC
每次这两个都会被同时提及,关系就好像KFC边上一定会有MC一样亲密的让人摸不到头脑。让人恍然大悟的词法作用域及作用域链讲解
看图说话才是王道viewport和1px | 工具人: 这是1px,设计师: 不,这不是
设计我不行,但吵架我在行啊可食用的「css布局干货」,纯Html示例,可调试 | 水平、垂直、多列
可观看,可调试,可带走,仅此一家,别无分店前端必须掌握的「CSS层叠上下文」讲解 | 纯手工示例,包教包会
妹子与猫,你要哪个?
参考文档