0%

背景

使用 Webpack 打包资源时体积过大,通常需要将基础库分离出来。

基础库分离

使用 html-webpack-externals-plugin

使用 html-webpack-externals-pluginreactreact-dom 基础库通过 CDN 引入,不打如 bundle 中

初始化项目

1
npm init -y

安装依赖

1
2
npm install --save react react-dom
npm install --save-dev webpack webpack-cli babel-loader @babel/core @babel/preset-env @babel/preset-react html-webpack-plugin html-webpack-externals-plugin

根目录下新建 webpack.config.js.bablerc

webpack.config.js 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const path = require("path");

const HtmlWebpackExternalsPlugin = require("html-webpack-externals-plugin");

module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
},
mode: "development",
module: {
rules: [
{
test: /.js$/,
use: "babel-loader",
},
],
},
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
module: "react",
entry:
"https://cdn.bootcss.com/react/16.13.1/umd/react.development.js",
global: "React",
},
{
module: "react-dom",
entry:
"https://cdn.bootcss.com/react-dom/16.13.1/umd/react-dom.development.js",
global: "ReactDOM",
},
],
}),
],
};

.bablerc 内容

1
2
3
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

根目录下新建 index.html

index.html 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.bootcss.com/react/16.13.1/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.13.1/umd/react-dom.development.js"></script>
<script src="./dist/main.js"></script>
</body>
</html>

修改 package.json

package.json 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"name": "webpack-externals",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/preset-react": "^7.9.4",
"babel-loader": "^8.1.0",
"html-webpack-externals-plugin": "^3.8.0",
"html-webpack-plugin": "^4.2.0",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
}
}

新建src/index.js

src/index.js 内容

1
2
3
4
5
6
7
8
import React from "react";
import ReactDOM from "react-dom";

function App() {
return <div>Hello, Webpack</div>;
}

ReactDOM.render(<App />, document.getElementById("root"));

执行npm start

此时的 dist 目录中的 main.js 就是只打包了我们自己写的 src/index.js 的内容

不想用 CDN,想使用 node_modules 中的怎么办

webpack.config.js 内容更改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const path = require("path");

const HtmlWebpackExternalsPlugin = require("html-webpack-externals-plugin");

module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
},
mode: "development",
module: {
rules: [
{
test: /.js$/,
use: "babel-loader",
},
],
},
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
module: "react",
entry: "umd/react.development.js",
global: "React",
},
{
module: "react-dom",
entry: "umd/react-dom.development.js",
global: "ReactDOM",
},
],
}),
],
};

会在ouput目录下新建一个vendor的目录, 将对应的两个文件拷过去,此时 html 文件中的script引用路径也需要更改

index.html 内容更改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/vendor/react/umd/react.development.js"></script>
<script src="./dist/vendor/react-dom/umd/react-dom.development.js"></script>
<script src="./dist/main.js"></script>
</body>
</html>

webpack 4 中还可以使用SplitChunksPlugin来提取公共资源

使用 SplitChunksPlugin

使用 optimization.SplitChunksPlugin

webpack.config.js 内容更改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const path = require("path");

module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
},
mode: "development",
module: {
rules: [
{
test: /.js$/,
use: "babel-loader",
},
],
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: "vendor",
chunks: "all",
},
},
},
},
};

会在output目录下生成一个vendor.js的文件,在 html 中引用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/vendor.js"></script>
<script src="./dist/main.js"></script>
</body>
</html>

写下这个题目之后,感觉是有点大言不惭的味道。毕竟我也不是大牛,甚至也不是个成手,只能算是个新手,写这样的话题,算是自己的目标吧。
不说别的,只说说我理解的初级前端,也就是可以开始做前端工作了,需要哪些条件。

  • 知道在哪里查看资料、找文档,具有基本的英文文档查找和阅读能力。
  • 在合理的时间内、使用HTML和CSS能够完成简单、常规的静态页面,并且有一定的可读性,可维护性。
  • 可以简单地使用浏览器的调试工具。
  • 能够使用Bootstrap/Semantic UI/Materializecss之类的常见CSS框架,搭配jQuery完成较为复杂的页面。
  • 了解Web应用的基本工作原理,比如HTTP请求和响应、常见的HTTP Verb和HTTP状态码等。
  • 了解服务器端编程技术,比如PHP或者Python的Django、Flask,或者Nodejs,可以做基本的CRUD(增查改删)的服务的处理。
  • 如果是给定了完整的RESTful的API,可以独立完成前端的CRUD(增查改删),对数据可以进行基本的处理,如查找、排序等。当然这个是可以依靠React或者Angular之类的JavaScript框架、库来完成。

** 文章是翻译自Medium,原文链接:Our Best Practices for Writing React Components **


我开始写React的时候就发现写组件有很多不同的方法,教程与教程之间的差别很大。尽管框架与那时相比已经完全成熟了,但是到目前还没有出现一个的被所有人都接受的“正确”书写方式。
在过去的几年里,我们团队写了大量的React组件,我们不断优化书写组件的方案,直到我们满意为止。这个指导代表了我们推荐的最佳实践,我们希望这能对新手和老手都能有帮助。
在开始之前,还有几点要说明的:

  • 我们使用了ES6和ES7语法。
  • 如果你还不了解presentational组件和container组件的区别,建议你先去看看这个。
  • 如果有什么建议、疑问或者反馈,请在评论区告诉我们。

基于类的组件

基于类的组件具有状态,通常会有一些方法,我们应该尽量少地使用基于类的组件,但它们有它们的应用场景。

接下来我们一行一行地构建我们的应用。

引入CSS

1
2
3
4
5
import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

我非常喜欢CSS in JavaScript的理念,但这到目前为止仍然是一个相对比较新鲜的想法,也没有一个成熟的解决方案,所以在那之前,我们为每个组件都导入一个CSS文件。
我们还会使用一个空行来开区分引入的dependency和从本地引入的内容。

初始化状态

1
2
3
4
5
6
7
8
import React, {Component} from 'react'
import {observer} from 'mobx-react'

import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

export default class ProfileContainer extends Component {
state = { expanded: false }

你也可以使用像这样的方法在constructor里初始化状态,我们倾向于使用更简单清晰的方式。
我们也会保证默认导出我们的class。

Read more »

React-Redux-Appointment

这个小应用使用Create React App创建,演示地址:https://liliang-cn.github.io/react_redux_appointment,repo地址:https://github.com/liliang-cn/react_redux_appointment

这是之前的React_appointment的Redux版,之前的演示,改写自Lynda的课程Building a Web Interface with React.js

文件结构

最终的文件目录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
react_redux_appointment/
README.md
node_modules/
package.json
public/
index.html
favicon.ico
src/
actions/
index.js
components/
AddForm.js
AptList.js
Search.js
Sort.js
constants/
index.js
containers/
AddForm.js
App.js
reducers/
apts.js
formExpanded.js
index.js
openDialog.js
orderBy.js
orderDir.js
query.js
index.css
index.js
Read more »

问题

现在我们有这样的需求,有一个由对象组成的数组,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let arr = [
{
name: 'Rosi',
age: 26
},
{
name: 'Reid',
age: 24
},
{
name: 'Morgan',
age: 25
},
{
name: 'Morgan',
age: 28
}
];

我们现在想要根据name来去重,也就是删除最后一个对象,怎么办呢?

方法1

使用lodash_.uniqBy():_.uniqBy(arr, 'name)

方法2

自己写一个函数:

1
2
3
4
5
function removeDuplicates(myArr, prop) {
return myArr.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
});
}

然后调用即可:removeDuplicates(arr, 'name)

React本身是特别简单的,因为它最初的目的就是搭建用户界面,对于复杂的应用来说,就需要借助Redux之类的JavaScript库来帮忙管理应用。

什么是Redux?

Redux是一个JavaScript库,用来管理应用的状态,可以提供可预测化的状态管理。

什么时候需要Redux?

你的应用是否有以下的需求:

  1. 复杂的数据流动;
  2. 组件间有互动;
  3. 非继承的数据;
  4. 大量的操作;
  5. 同样的数据在多处使用。

如果有一项或一项以上,就可以考虑使用Redux。

Redux的核心要点

我们先不要管那些听上去就云里雾里的storeactionreducer,而是捋一捋一个应用的使用,这里就前一篇文章中新建的Todos为例:

Read more »

React Todos

了解了React基础之后,得做个应用,首选Todos。做一个这样的Todos:最终效果图

新建工程

执行create-react-app react_todos,新建一个工程,然后安装包:npm install express body-parser ejs mongojs material-ui axios --save

配置服务器和数据库

在工作目录下新建server.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;
const path = require('path');
const bodyParser = require('body-parser');
// const cors = require('cors');
const ejs = require('ejs');

// 引入路由
const index = require('./routes/index');
const todos = require('./routes/todos');

// 渲染引擎
app.set('view engine', 'ejs');
app.engine('html', ejs.renderFile);

// 跨域请求处理
// app.use(cors());

// 配置静态文件
app.use(express.static(path.join(__dirname, 'build')));

//配置body-parser用以解析请求
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// 配置路由
app.use('/api', todos);
app.use('/', index);

// 启动服务并监听8080端口
app.listen(PORT, () => {
console.log(`Server started on port ${PORT}`);
});
Read more »

React的设计思想

React是基于具有特性(props)、状态(state)和生命周期方法(lifecycle methods)的组件来构建用户界面的JavaScript库。

组件化

什么是组件化?就像我们造汽车一样,分别制造车身、动力总成,底盘等,然后组装起来,并且各个系统又是由各自的子系统组装而成。

props和state

我在这里将它们分别称为特性和状态,什么叫特性?就是那些能把你跟别人区分出来的信息,比如你的出生日期,你的性别,你的父母等,这些信息是不会轻易发生改变的。状态则指的是当前的信息,比如刚大学毕业,没有女朋友,这些信息以后有可能会发生变化。我们通过特性和状态就可以来描述一个人。
来到我们的Web中,我们把若干个具有自己的特性和状态单元组合在一起就可以呈现出完整的界面来。

生命周期方法

就像一辆汽车一样,它会经历多个阶段,从制造到销售到使用再到最后的报废,我们的组件有也会有生命周期,用来获取组件的信息,或者对发生的变化做出反应,比如获得某个特性的时候,有状态更新的时候。一般来说,所有的组件都会经历初始化、运行中和销毁三个阶段,每个阶段都有开发者可以自定义的函数,执行不同的行为。

Read more »

now

**以下内容翻译自now

now是一个实时的部署系统,使用now,可简单、快速、可靠地部署Node.js或者Docker驱动的网站,应用和服务。
实际上,任何包含一个package.json或者Dockerfile的目录都可以通过一条now命令来传输到云端。

每次发布一个项目的时候,now会给项目分配一个唯一的URL(在传输完成之前就会分配),生成的URL大概会是这个样子的:my-app-erkgfjtrna.now.sh
等到了项目正式上线的时候,指定合适的别名就行了。
我们可以把now当做动态的代码(微服务和服务器端程序)内容分发网络。

安装

通过npm安装now:

1
$ npm install -g now

第一个项目

在终端中执行:

1
2
$ mkdir my-project
$ cd my-project
Read more »