zoukankan      html  css  js  c++  java
  • 搭建简易React

    一步一个脚印搭建简易React

    准备环境:

    1. node环境
    2. vscode编辑器

    初始化项目

    // 创建一个文件夹,当然你也可以手动创建
    mkdir my-react
    // 进入到项目目录
    cd my-react
    // 生成pakeage.json文件,这个文件主要是用来记录这个项目的详细信息的,它会将我们在项目开发中所要用到的包,以及项目的详细信息等记录在这个项目中
    npm init
    复制代码

    搭建项目环境

    由于我们的目的是实现一个简易的React,为了能把更多的重心花在React上,我们需要借助一些工具来帮助我们处理一些React之外的问题。

    1. webpack: 我们需要使用webapck将我们的项目打包,最终生成一个js文件
    2. babel:能帮助我们将高级的ES语法向下转化成浏览器识别的低版本的ES语法

    webpack

    cnpm install --save-dev webapck webapck-cli
    复制代码

    然后在我们项目的根目录新建webapck.config.js文件来告诉webapck如何打包我们的js文件

    module.exports = {
      entry: './src/main.js',
    };
    复制代码

    同时创建src目录,并且新建main.js作为项目的入口文件

    // main.js
    const array = [1, 2, 3, 4, 5];
    array.find((item) => item === 1);
    复制代码

    babel

    babel需要安装的包比较多

    cnpm install --save-dev babel-loader @babel/core @babel/preset-env
    复制代码
    1. babel-loader: 使用babel-loader处理js文件,会将es5以上的语法进行转义
    2. @babel/core: 封装了babel-loader需要用到的api
    3. @babel/preset-env: babel 内部经历了「解析 - 转换 - 生成」三个步骤。而 @babel/core 这个库则负责「解析」,具体的「转换」和「生成」步骤则交给各种插件(plugin)和预设(preset)来完成

    tips1: 使用@babel开头是为了声明作用域

    tips2: @babel/preset-*实际上就是各种插件的打包组合,也就是说各种转译规则的统一设定,目的是告诉loader要以什么规则来转化成对应的js版本

    好了,回到我们的项目本身,我们需要告诉webapck,用babel-loader去打包我们的js文件,以及babel-loader对应的配置。

    module.exports = {
      entry: {
        main: "./src/main.js",
      },
      module: {
        rules: [
          {
            test: /.js$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ["@babel/preset-env"],
              },
            },
          },
        ],
      },
    };
    复制代码

    第一次打包编译

    在终端执行npx webpack,就可以在dist文件加下查看打包出来的文件了

    tips: 使用npx可以保证我们执行的是当前项目

    my-react
    │ 
    │
    └───dist
    │   │   main.js
    └───src
    │   │   main.js
    └───package.json
    |
    └───webpack.config.js
    复制代码

    引入babel插件支持jsx语法

    现在我们尝试在main.js中声明一个不一样的变量,然后再执行npx webpack

    const array = [1, 2, 3, 4, 5];
    
    array.find((item) => item === 1);
    
    const ele = <div id="id" class="mr5" >
      <span> zaoren </span>
    </div>;
    console.log(ele);
    复制代码

    我们看到报错了,原因是我们不能解析jsx语法

    因此,我们引入另一款babel插件来帮助我们解析jsx语法 - @babel/plugin-transform-react-jsx

    cnpm install --save-dev @babel/plugin-transform-react-jsx
    复制代码

    然后在我们的webapck.config.js中引入插件

    {
        test: /.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ["@babel/preset-env"],
            plugins: ['@babel/plugin-transform-react-jsx']
          },
        },
      },
    复制代码

    然后再用webapck打包一下,发现没有报错,打包成功了!

    然后我们尝试着用一个main.html来引入我们打包后的main.js

    <!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>
      <script src="./main.js"></script>
    </body>
    </html>
    复制代码

    但是当我们在浏览器中运行的时候发现报了个错误

    咦?React未定义,想想我们这篇文章的主题是什么?不就是实现一个简易的React吗?所以,到现在为止,我们不进一步使用工具来帮助我们简化工作量。完全靠自己了!!!

    开始动手写React

    从打包后的代码可以看出,首先,我们需要一个React变量,React变量的createElement方法接受了三个参数

    1. DOM节点类型
    2. DOM节点上的属性对象
    3. DOM上的子节点children

    我们来简单动手实现一下React这个对象,并且把创建好的DOM对象挂载到html页面的body上https://www.douban.com/group/topic/198335398/

    // main.js
    let React = {
      createElement: (tagName, attributes, ...children) => {
        let ele =  document.createElement(tagName);
          
        Object.keys(attributes || {}).forEach(key => {
          ele.setAttribute(key, attributes[key]);
        });
        
        children.forEach(child => {
          ele.appendChild(child);
        })
    
        return ele;
      },
    };
    
    const ele = <div id="id" style="background: red" >
      <span>zaoren1</span>
      <span>zaoren2</span>
    </div>;
    
    document.body.appendChild(ele);
    
    复制代码

    执行npx webpack之后,我们打开浏览器发现报错了!

    调试后发现,我们这个t为文本“zaoren”,并且原生的Web API中createElement这个方法是不支持添加文本节点的,需要使用createTextNode方法(详情见MDN)

    因此在创建子节点的时候,需要对是否是文本节点进行判断。

    children.forEach(child => {
      if (typeof child === "string") {
        child = document.createTextNode(child);
      }
      ele.appendChild(child);
    })
    复制代码

    重新npx webpack,查看效果 可以看到我们的<div>子节点<span>以及我们的一些属性都能成功设置啦!恭喜你,已经完成了第一步!

    与属性相对应对的是函数,但目前我们的React还是没有处理事件的能力的,那么只需要在attribute中过滤出函数属性,然后增加事件监听器(这里简单用正则去匹配on字符串)

    ... 省略部分
    Object.keys(attributes || {}).forEach(key => {
      if (key.match(/^on/)) {
        let eventType = key.replace(/^on/, '').toLocaleLowerCase();
        ele.addEventListener(eventType, attributes[key]);
        return
      }
      ele.setAttribute(key, attributes[key]);
    });
    
    ... 省略部分
    const ele = <div id="id" style="background: red" >
      <span onClick={() => {console.log('add event success!')}}>zaoren1</span>
      <span>zaoren2</span>
    </div>;
    复制代码

    点击zaoren1,可以看到我们已经成功添加click事件啦! https://www.douban.com/group/topic/198377234/

    项目源代码

    本文使用 mdnice 排版


    作者:枣仁,
    链接:https://juejin.im/post/6886708531903496205
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    MysQL使用一与Python交互
    WPF三大模板简介
    Java Servlet生成JSON格式数据并用jQuery显示
    JSP之应用Servlet过滤器进行身份验证
    Java调用SQL Server存储过程
    JSP之Cookie对象使用
    JSP之response对象使用
    JSP之静态include指令、动态Include指令
    JSP之使用useBean、setProperty、getProperty指令
    jspSmartUpload使用初步
  • 原文地址:https://www.cnblogs.com/5118svip/p/13865201.html
Copyright © 2011-2022 走看看