徒手撸个react项目框架(上)

最近学习react.js,发现项目框架除了使用的js库不一样(vue.js、react.js),配置基本上是大同小异的css

徒手撸个vue项目框架(上)

徒手撸个vue项目框架(下)

徒手撸个react项目框架(上)

徒手撸个react项目框架(下)

1、准备工做

1.新建reactProject文件夹

进入根目录,初始化项目html

cd reactProject

npm init -y // -y是采用默认配置
复制代码

此时目录出现package.json文件前端

2.建立项目结构

在根目录下新建src文件夹,在src下暂时新建名为index的js文件做为入口文件vue

根目录下建立一个index.html,做为入口页面node

3.使用webpack

下载webpack时你可能会出现无限下载webpack-cli的问题,这是由于你没有先全局安装webpack和webpack-cli的缘由react

// webpack4.X开始webpack-cli被提出来做为一个独立的包了
// 在下载webpack同时也要下载webpack-cli,且必须同时下载不然会报错,由于版本不匹配
cnpm install webpack-cli webpack --save-dev
复制代码

webpack默认只能打包js模块,它能够将你写的多个js模块打包成一个js文件,最后在入口页面引入它webpack

webpack4开始默认大于配置,换句话说能够不用再引入一个配置文件来打包项目,所以他有不少默认值web

默认入口文件是src下的index.js,输出为dist目录下的main.js(假如没有dist目录会自动建立)npm

可是它仍然是高配置的,假如须要咱们只需在项目根目录下新建webpack.config.js来进行一切的配置json

相比于webpack4以前的版本,它的配置项多出一个mode选项,可选值为"development" 或 "production"(默认),它们的区别就是development打包输出的文件不是压缩版本的

4.使用react.js

react和vue不一样的是,react使用两个包协同工做

  • react包:负责组件或者虚拟dom
  • react-dom包:负责将组件或者虚拟dom插入到根节点
cnpm install react react-dom --save-dev
复制代码

index.js中

// index.js
import React from 'react'
// 这个包名必须这样写
import ReactDOM from 'react-dom'
/* *createElement: 建立虚拟dom元素 * 第一个参数为标签类型 * 第二个参数为标签属性对象 * 剩余参数皆为参数为子节点 */
const dom = React.createElement("h1",{id: "test"},"hello react")
// render函数是将虚拟dom插入目标容器Target container 
ReactDOM.render(
    dom,
    document.getElementById("root")
)
复制代码

index.html中

//  index.html
<div id ="root"></div>
复制代码

页面中会看到hello react的字样,审查元素以下

<div id="root"><h1 id="test">hello react</h1></div>
复制代码

这说明咱们的准备工做都成功完成了

2、完善框架功能

1.使用webpack-dev-server

每次写完新的内容要想看到效果,就必须使用webpack进行打包,咱们更但愿当代码改变时自动打包编译

webpack-dev-server能够帮咱们作到!

a.下载
cnpm i webpack-dev-server --save-dev
复制代码
b.使用

咱们不能像使用webpack命令同样使用webpack-dev-server命令,由于webpack-dev-server是局部安装的,而令行里只能使用全局安装的包,使用局部安装的包,咱们须要使用在package.json中配置scripts

// package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --hot"
},
复制代码

而后再命令行使用npm run dev

npm run dev
复制代码

注意看下面的节选的代码

npm run dev

> vueproject@1.0.0 dev C:\myProject\reactProject
> webpack-dev-server

i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /
i 「wds」: Content not from webpack is served from C:\myProject\reactProject
i 「wdm」: Hash: e70fb3ae9bf074915cad
Version: webpack 4.35.0
复制代码

从这里咱们知道两件事: 首先,咱们的项目运行在本机8080端口,其次webpack的output输出在根目录下,因此记得修改index.html中引入main.js的路径,不然你是看不到新的效果的

可是咱们在根目录下并无看到这个文件,这是由于它被放在内存中(这样的读写速度快),而不是磁盘中,另外咱们还能够修改端口,甚至能够在编译完成后自动打开浏览器

它具体的配置能够是在webpack的devServer

devServer:{
    host: '127.0.0.1',
    port: 8081,
    open: true
}
复制代码

也能够是在cli里,这是最暴力的方式,可是端口仍是放在devServer里,方便之后项目的配置

// package.json
"scripts": {
    "dev": "webpack-dev-server --open --port 30000"
},
复制代码

2.使用html-webpack-plugin

既然将main.js放在内存中能够加快读写速度,那是否是把页面放在内存中能够进一步加快读写速度了?

答案是确定的!使用html-webpack-plugin就能够作到

a.下载
cnpm i html-webpack-plugin --save-dev
复制代码
b.使用
// webpack.config.js
const htmlWebpackPlugin = require('html-webpack-plugin')
...
plugins: [
    new htmlWebpackPlugin({
      template: path.join(__dirname, "./index.html"),
      filename: "index.html"
    })
  ],
复制代码

上面的代码是根据磁盘中的index.html在内存中生成一个index.html,咱们在浏览器中审查页面发现会多一个script标签,这是插件自动将内存中的main.js加入到内存页面中了,因此咱们这是应该删除手动添加的script标签

// index.html
<body>
    <div id="app"></div>
    <!--删除或者注释掉 <script src="./main.js"></script> -->
</body>
复制代码

如今它已经能够本身跑起来并自动监听变化作出反应了

3、继续完善框架(项目)功能

1.使用jsx语法

虽然开起来已经很完美了,可是像前面面那样写react虚拟dom老是以为很复杂,做为前端工程师,咱们更但愿写的dom就是这样的,而后把它和以前同样插入

const dom = <h1 id="test">hello react</h1>
ReactDOM.render(
    dom,
    document.getElementById("root")
)
复制代码

这彻底是能够的,上面的写法就是react的扩展语法jsx,使用jsx语法必须使用babel-loader

a.下载

有了前面文章的经验,我发现若是如今下载babel-loader最好使用下面的形式,由于babel已经更新到7.x.x了,写法和以前的版本有很大差别,不然老是会出现一些版本不兼容的错误

//下载loader加载器
cnpm i @babel/core babel-loader --save-dev
//下载插件
cnpm i @babel/plugin-transform-runtime --save-dev
//下载预设
cnpm i @babel/preset-env @babel/preset-react --save-dev
复制代码
b.使用

babel的两种使用方法前面已经说过了,这里再简单的说说官网给出的别的简单方法

第一种方法,在rules中配置babel-loader同时提供options选项

// 第一种写法。
//除了基本的配置test外,可使用options,里面必须有presets和plugins两个选项
// presets是预设的babel语法转换规则,这里除了使用最新的规则外仍是用了针对react的jsx转换规则
// plugins是指明使用的插件
module: {
    rules: [
      {
        test: /\.js|jsx$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
            plugins: ["@babel/plugin-transform-runtime"]
          }
        }
      }
    ]
  },
复制代码

第二种方法根目录新建.babelrc文件,格式同json

{
    presets: ["@babel/preset-env", "@babel/preset-react"],
    plugins: ["@babel/plugin-transform-runtime"]
}
//它会自动识别到这个文件
//此时能够删掉第一种方法中的options选项
复制代码

第三种方法在package.json中添加babel节点

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": ["@babel/preset-env", "@babel/preset-react"],
    "plugins": ["@babel/plugin-transform-runtime"]
  }
}
复制代码

第四种方法是根目录新建.babelrc.js 配置和前面都同样,只是可用js编写

const presets = ["@babel/preset-env", "@babel/preset-react"];
const plugins = ["@babel/plugin-transform-runtime"];
// 这种写法的好处是你能够调用node API
if (process.env["ENV"] === "prod") {
  plugins.push(...);
}
module.exports = { presets, plugins };
复制代码

固然还有cli方法,或者选择转换node_modules目录的babel.confog.js的方法,但都是比较复杂或者冷门的

2.使用样式表

在src下新建了一个css样式表文件夹,里面写了本身针对本身组件的样式,而后在须要的jsx文件中引入它,像我这样

/* css/index.css文件 */
.test{
    color: green   
}
复制代码
//在index.jsx文件中引入样式
import React from "react";
import "@/css/index.css"

export default function Index() {
    return <div className="test" id="index">index page</div>;
}

//在login.jsx中不引入样式,可是添加class
import React from "react";

export default class Login extends React.Component {
  render() {
  // 由于class是js的关键字,因此jsx中使用className做为html的class
    return <div className="test">login page</div>;
  }
}
复制代码

这时候会报错,提示使用合适的loader

和vue同样要使用样式表必须使用loader,由于它们都是jsx语法生成虚拟dom的,而jsx没法解析样式表

a.下载
cnpm i style-loader css-loader --save-dev
复制代码
b.使用

下载完以后和vue项目同样配置文件

rules: [
    ...
    {
        test: /\.css$/,
        use: ["style-loader","css-loader"]
    }
]
复制代码
c.问题

虽然这时候可使用样式了可是,你会发现你没有引入样式的login组件页面也是绿色字体了

这是由于webpack会将全部的样式放在一个style标签而后插入head标签下,它的做用域是全局了,审查元素你看到的是这样的

一样的问题,vue能够在style标签添加scoped指令来控制样式做用域,原理就是在在样式表中选择器前加个组件的标识,react中没有指令的概念,怎么解决了这个问题了?

好在css-loader能够提供模块化功能,咱们只需以下改动

rules: [
    ...
    {
        test: /\.css$/,
        // 这种写法和url带参数很相似,这是开启模块化的意思
        use: ["style-loader","css-loader?modules"]
    }
]
复制代码

此时咱们的引入css样式须要一个参数去接收css暴露出来的模块(固然这是css-loader的功能)

//在index.jsx文件中引入样式
import React from "react";
import indxCss from "@/css/index.css"
console.log(indxCss) // => {test: "q7KCiLIWvHKVJp6HMfV2y"}
export default function Index() {
    return <div className={indxCss.test} id="index">index page</div>;
}
复制代码

同时说个有意思的事,css-loader除了modules参数外,还有不少参数 若是你不喜欢随机的字符串作样式的标识,能够设置localIdentName参数,它是如下方式的组合

  • path: 文件的路径
  • name: 文件名称
  • local:样式名称
  • hash:32哈西值,后面能够定义长度
rules: [
    ...
    {
        test: /\.css$/i,
        // 下面这种写法是老版本的写法,会一直报错无效的版本,别问我是怎么知道的,webpack官网没有提示
        //use: ["style-loader","css-loader?modules"],
        // 新版本3.0.0是这样的
        // 去css-loader的npm官网才能看到这种写法
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[path][name]-[local]-[hash:base64:5]"
              }
            }
          }
        ]
    }
]
复制代码

虽然如今看上去是完美的,可是有个问题是假如如今引入第三方库,它也是css文件,也会被模块化的,但咱们但愿它是全局有效的,比较好的作法就是第三方库采用css样式,而本身的样式启用scss或者less,因此你得安装它们的loader

cnpm i less-loader --save-dev
复制代码

添加loader

rules: [
    ...
    {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
     },
    {
        test: /\.less$/i,
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[path][name]-[local]-[hash:base64:5]"
              }
            }
          },
          {loader: "less-loader"}
        ]
    }
]
复制代码

3.使用图片

若是在样式中添加以下样式

.test{
    background-image: url(../imgs/11.jpg)
}
复制代码

会发现jsx也没法处理图片url的,因此得添加loader去处理这些

a.下载

url-loader内部使用了file-loader,因此两个loader要同时下载

cnpm i url-loader file-loader --save-dev
复制代码
b.使用

基本的使用以下

module: {
    rules: [
        ...
        {
            test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
            loader: "url-loader"
        }
    ]
  },
复制代码
c.options

这个和css-loader设置同样

module: {
    rules: [
      {
        test: /\.(jpg|png|jpeg)$/,
        loader: "url-loader",
          options: {
            limit: 8000,    // 当文件字节大小超过限定值时触发后面的设置
            name: "[hash:8]-[name].[ext]"  // 这是在原先的名称和后缀名前加了八位的哈希码
          }
      }
    ]
  },
复制代码

4、结语

目前基本的功能都有了,可是仍是不够完美,下期将会引入react-router,对业务进行封装。从目前来看,无论vue项目框架仍是react框架,有极大的类似之处,学了vue学react就简单多了,并且这是学习react对vue的理解也上升了一个层次。

若是你也作到这里相信你也会有这种感觉

努力、奋斗

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息