概念区分:
library(库):小而巧的库,只提供了特定的API。Framework(框架):大而全的是框架;框架提供了一整套的解决方案(全家桶);react和vue做对比:(对比不完全)
组件化方面:模块化:是从代码角度分析,把一些可复用的代码,抽离为单个的模块;便于项目的维护和开发;模块化的好处: 解决命名冲突:在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题提高复用性提高代码可维护性 组件化:是从UI界面角度分析,把一些可复用的UI元素,抽离为单独的组件;便于项目的维护和开发;组件化的好处: 随着项目规模的增大,手里的组件越来越多;很方便就能把现有的组件,拼接为一个完整的页面; vue组件化开发:通过 `.vue` 文件,来创建对应的组件;template模板 script行为 style样式react组件化开发:React中有组件化的概念,但是,并没有像vue这样的组件模板文件;React中,一切都是以JS来表现的;因此要学习React,JS要合格;ES6 和 ES7 (async 和 await) 要会用;开发团队方面React是由FaceBook前端官方团队进行维护和更新的;因此,React的维护开发团队,技术实力比较雄厚;Vue:第一版,主要是有作者 尤雨溪 专门进行维护的,当 Vue更新到 2.x 版本后,也有了一个以 尤雨溪 为主导的开源小团队,进行相关的开发和维护;移动APP开发体验:React,结合 ReactNative,也提供了无缝迁移到 移动App的开发体验Vue,结合 Weex 这门技术,提供了 迁移到 移动端App开发的体验REACT核心概念
虚拟DOM(Virtual Document Object Model)DOM本质:浏览器中的概念,用JS对象来表示 页面上的元素,并提供了操作 DOM 对象的API;虚拟DOM:是框架中的概念,是程序员 用JS对象来模拟 页面上的 DOM 和 DOM嵌套;作用:为了实现页面中, DOM 元素的高效更新用JS模拟DOM元素: <div id="first" class="myfirst"> this is a test! <p>test2</p> </div> var div={ tagName:"div", attrs:{ id:"first", class:"myfirst" }, //模拟子节点: children:{ "this is a test!", { tagName:"p", attrs:{} children:{ "test2" } } } }react的Diff算法:(具体没细看呢)
tree diff:新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;component diff:在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;(比如一个轮播图组件)element diff:在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;(一个轮播图组件肯定是由很多元素组成的,比如div等等的对比)快速创建一个webpack4.x的项目:
1. 运行`npm init` 快速初始化项目(生成package.json文件 2. 在项目根目录创建`src`源代码目录和`dist`产品目录 3. 在 src 目录下创建 `index.html`,并且引入<script src="../dist/main.js"></script> 4. 使用 cnpm 安装 webpack ,运行`cnpm i webpack webpack-cli -D` + 如何安装 `cnpm`: 全局运行 `npm i cnpm -g` 5.创建webpack配置文件:webpack.config.js 6.创建src下的index.js文件(它是入口文件) 7.按自己所需配置package.json下的scripts属性:运行脚本的npm命令行缩写
8.为了实现MHR所以安装webpack-dev-server,配置项可以写在webpack.config.js里面或者通常写在package.json的scripts里的dev里面。
9.scripts的dev里配置其他:--open 自动打开浏览器 --hot模块热替换 --host修改域名 --port修改端口号。 注意:webpack 4.x 提供了 约定大于配置的概念;目的是为了尽量减少 配置文件的体积; + 默认约定了: + 打包的入口是`src` -> `index.js` + 打包的输出文件是`dist` -> `main.js`(当然这两个文件可以额外配置,只不过就是覆盖了默认配置罢了) + 4.x 中 webpack.config.js新增了 `mode` 选项(为必选项),可选的值为:`development` 和 `production`;(如果不修改默认,那么配置文件里只写mode就可以跑了)
+webpack.config.js里面的的一个小技巧:带s的都是数组,不带s的都是对象
+进行到步骤8之后发现热替换其实没有成功,因为修改index.js里面的内容并没有实现更新,因为webpack-dev-server打包好的main.js是托管到了内存中,所以在项目根目录中看不到;但是可以认为,在项目根目录里有一个看不见的main.js,此时我们修改index.html里面的script的src为src="/main.js",可以发现更新了。
这样也带来一个问题思考。加入main.js没有托管到内存中而是放在物理磁盘里面,那么我们每次修改index.js都要访问磁盘,显然低效且损耗大,所以webpack-dev-server把打包好的main.js托管到了内存里,而index.html却没有托管进去!把首页也托管到内存里则:
10.使用插件html-webpack-plugin,(插件使用:导入-->实例化-->放到暴露出去的配置对象的plugins数组里),该插件能自动加上位于根目录下的script所以不必再用步骤8中手动添加script了!
在项目中使用react
基本步骤:install react react-dom-->index.js引入包react react-dom-->创建虚拟DOM元素、渲染元素(中间有一步去index.html里面创建div容器)-->
react:专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中react-dom:专门进行DOM操作的,最主要的应用场景,就是`ReactDOM.render()`React.createElement();参数列表 字符串类型的参数,表示要创建的标签的名称对象类型的参数, 表示 创建的元素的属性节点,没有属性内容就写null即可子节点(包括其他虚拟DOM ,获取,文本子节点)参数n:其他子节点 ReactDOM.reader()参数列表: 表示要渲染的虚拟DOM对象指定容器,这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象(因为webpack(的 htmlwebpackplugin插件)会把将index.js打包好为main.js注入到index.html里面所以直接document.getElementById即可)---->使用上述方式其实写标记语言非常麻烦,而js文件中不能写这种类似于HTML的标记否则打包失败,可以使用babel来转换这些JS中的标签---->JSX语法:本质还是在运行的时候转换成了React.createElement()形式执行
基本步骤续:
运行`cnpm i babel-core babel-loader babel-plugin-transform-runtime -D`(转换操作)
运行`cnpm i babel-preset-env babel-preset-stage-0 -D`(语法操作)
安装能够识别转换jsx语法的包 `babel-preset-react`
webpack.config.js配置babel-loader:(千万别忘记添加 exclude 排除项)-->配置.babelrc文件-->配置resolve,extensions让项目中使用某些文件时可以省略文件名(.js .jsx .json),以及alias配置根目录别名:这样每次使用的时候尽量用@引入根目录下的组件,避免路径出现错误
JSX语法:
本质:并不是直接把 jsx 渲染到页面上,而是 内部先转换成了 createElement 形式,再渲染的;JS代码写到 { } 中,xml语法写到<>里面字符串数组转换成jsx语法采坑 const arr=['beijing','shanghai']; const namearr=[]; arr.forEach((item)=>{ const temp=`<h2>${item}</h2>`//这样并不可以 因为只是单纯字符串 namearr.push(temp) }) //应该是: <h2>{item}</h2> //然后在ReactDOM.render里写{{namearr}} //在ReactDOM.render里面直接写 arr.map((item)=>{ return <h2>{item}</h2> //一定要写return 不然会渲染不出来, //或者说只有一个返回值时可以省略return和花括号,arr.map((item)=> <h2>{item}</h2>) //坑:切记不要 写了花括号又没写return这种半截语法!!! })注意:react中,需要把key添加给被forEach或map或for循环直接控制的元素:key={item};
关于jsx注释:推荐使用`{ /* 这是注释 */ }`为 jsx 中的元素添加class类名:需要使用`className` 来替代 `class`;`htmlFor`替换label的`for`属性(for 属性规定 label 与哪个表单元素绑定。)在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;(vue也是)在 jsx 语法中,标签必须 成对出现,如果是单标签,则必须自闭和!React创建组件的方式
构造函数方式创建组件,使用时用标签即可;用**构造函数**创建出来的组件:叫做“无状态组件” 首字母一般大写必须返回一个合法的jsx虚拟DOM元素或者返回null,则表示此组件是空的,什么都不会渲染传参方式:在function People(props)参数接收后直接使用即可:props.xxx没有私有数据(this.state) 使用class关键字创建组件: 自定义构造器中必须使用super继承父类构造器必须包含render函数:渲染当前组件对应的虚拟DOM元素。且必须返回可用的虚拟DOM元素传参方式:不需要格外接收,直接this.props.xxx使用即可拥有私有数据:this.stateclass组件内部this表示:当前组件的实例对象 有关于ES6的class创建类(面向对象编程的新方式)时的注意点: constructor基本使用:(可参考阮一峰老师的ES6入门):但凡创建了一个类在实例化(new)它的时候就会调用constructor,如果不写就自动生成一个。实例属性:定义在constructor中的属性,且使用this挂到实例对象上的属性;实例方法:不被static关键字修饰的方法,实例对象可以调用。静态属性:使用static关键字修饰的属性,调用时People.count;静态方法:被static关键字修饰的方法,调用时People.show();class的继承:使用extends关键字实现继承,如果没有写constructor,那么可以直接继承父类的实例属性和实例方法。一旦要自定义构造器,则一定要写super(name,age)来继承父类的构造器。具体来说:ES5中继承是先创建子类的实例对象this,然后把父类方法添加到this上面(Parent.apply(this)),所以出现了两次调用父类构造函数的现象(,或者称父类覆盖子类),而ES6子类是没有实例对象this的必须先用super继承,否则没有this可用。注意:
在 class 的 { } 区间内,只能写 构造器、静态方法和静态属性、实例方法;
在类内部使用componentDidMount函数时有时需要用到静态方法静态属性,在这个钩子里使用则直接this.constructor.打点掉用或者解构赋值即可,注意:this.constructor就是这个类(所以能用啊)
class其实只是个语法糖,class 关键字内部,还是用 原来的配方实现的;
两种创建组件的方式对比:
利用构造函数创建的组件:叫做“无状态组件”利用class关键字创建的组件:叫做“有状态组件”state有无,生命周期函数有无props使用props和state(vue:data)对比:
props是外界传过来的值,且只读;注:不管是vue还是react,组件中的props永远是只读的不能被重新赋值state是组件内部的状态,可读可写;组件中样式的使用:
行内样式使用:普通写行内样式直接字符串形式就可以了,jsx语法中必须写成style={{color:'red'}}对象形式(vue也是对象形式,但是是单{}),带有连字符的属性写成驼峰样式{{fontSize:'15px'}},因为属性允许含有连字符但是要写成:‘font'+'-'+'size'如果是数值类型的样式可以不用引号包裹,如果是字符串类型的样式值必须用引号包裹;style={{ color: 'red', fontSize: '35px', zIndex: 3}}样式抽离:抽离为js文件,里面直接是style对象,使用的地方引用即可。样式抽离:抽离为单独的样式表模块:css文件,引入style-loader css-loader并且配置rules;问题:直接导入 css 样式表,默认是在全局上,整个项目都生效; 思考:Vue 组件中的样式表,有没有 冲突的问题???答案: Vue 组件中的样式表,也有冲突的问题;但是,可以使用 <style scoped></style>有了作用域疑问:React 中,有没有类似于 scoped 这样的指令呢?答案:没有;因为 在 React 中,根本就没有指令的概念;可以在 css-loader 之后,通过 ? 追加参数,其中,有个固定的参数,叫做 modules , 表示为 普通的 CSS 样式表,启用模块化。但是:css模块化只针对类名和ID名起作用在webpack.config.js中使用`localIdentName`自定义生成的类名格式,可选的参数有:
- [path] 表示样式表 `相对于项目根目录` 所在路径 - [name] 表示 样式表文件名称 - [local] 表示样式的类名定义名称 - [hash:length] 表示32位的hash值 - 例子:`{ test: /\.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]'] }` LOCAL IDENT NAME
在css文件中使用 `:local()` 和 `:global()`:
- `:local()`包裹的类名,是被模块化的类名,只能通过`className={cssObj.类名}`来使用
同时,`:local`默认可以不写,这样,默认在样式表中定义的类名,都是被模块化的类名;
- `:global()`包裹的类名,是全局生效的,不会被 `css-modules` 控制,定义的类名是什么,就是使用定义的类名`className="类名"`
如果在引用某个包的时候,这个包被安装到了 node_modules 目录中(bootstrap), 则,可以省略 node_modules 这一层目录,直接以包名开始引入自己的 模块 或 样式表import bootcss from 'bootstrap/dist/css/bootstrap.css' 见步骤5,但是设置css的时候已经设置了模块化,在使用的时候显然不方便:多个类名必须用数组join方法变成一串类名 那么我们可以自己规定:
第三方的 样式表,都是以 .css 结尾, 这样,我们不要为 普通的 .css 启用模块化(此时引入bootstrap:import 'bootstrap/dist/css/bootstrap.css' 即可,用的时候不用对象了直接上字符串单引号);而自己的样式表,都要以 .scss 或 .less 结尾, 只为 .scss 或 .less 文件启用模块化
样式抽离:使用bootstrap ;安装url-loader(webpack学习笔记-2-file-loader 和 url-loader)-->引入import bootcss from 'bootstrap/dist/css/bootstrap.css'样式抽离:使用less scss;安装sass-loader 以及它依赖的node-sass-->配置rules启用模块化以及类名规范
React 中绑定事件:
事件的名称都是React的提供的,因此名称的首字母必须大写`onClick`、`onMouseOver`(小驼峰命名)为事件提供的处理函数,必须是如下格式 :onClick= { function } 直接在里面写匿名函数,只接受function,不接受函数返回值即(函数的调用即myClickHandler())在类里定义实例方法,然后要在类里使用必须用this:onClick={this.myClickHandler}用的最多的事件绑定形式为:箭头函数+箭头函数赋值给函数名 <button onClick={ ('传参') => this.show('传参') }>按钮</button> //其实这种并不方便,因为有参数的时候需要一致前后有参数 //建议: <button onClick={ this.show }>按钮</button>//的确需要传参的时候传参 // 事件的处理函数,需要定义为 一个箭头函数,然后赋值给 函数名称 show = (arg1) => { console.log('show方法' + arg1) } 在React中,如果想要修改 state 中的数据,推荐使用 `this.setState({ })`★在this.setState({})中只会把对应的state状态更新,而不会覆盖其他的state状态★this.setState({})方法的执行是异步的,如果在this.setState({})修改状态后,又想拿到最新的state状态需要使用this.setState({},callback)在callback回调里面拿即可绑定文本框与state中的值:react是单向数据流:在 Vue 中,默认提供了`v-model`指令,可以很方便的实现 `数据的双向绑定`;,在 React 中,默认只是`单向数据流`,也就是 只能把 state 上的数据绑定到 页面,无法把 页面中数据的变化,自动同步回 state ; 如果需要把 页面上数据的变化,保存到 state,则需要程序员手动监听`onChange`事件,拿到最新的数据,手动调用`this.setState({ })` 更改回去;(即react实现数据双向绑定)注:onChange事件中获取文本框的值有两种方案: 通过事件参数e(e.target.value)React 中,也有 `ref`, 在要获取的元素内添加ref,获取时用`this.refs.引用名称`(和 Vue 中差不多,vue 为页面上的元素提供了 `ref` 的属性,如果想要获取 元素引用,则需要使用`this.$refs.引用名称`)生命周期函数:
生命周期的概念:每个组件的实例,从 创建、到运行、直到销毁,在这个过程中,会出发一些列 事件,这些事件就叫做组件的生命周期函数;
vue生命周期函数 react生命周期函数- **组件创建阶段**:特点:一辈子只执行一次
> componentWillMount: > render: > componentDidMount:
- **组件运行阶段**:按需,根据 props 属性 或 state 状态的改变,有选择性的 执行 0 到多次
> componentWillReceiveProps: > shouldComponentUpdate: > componentWillUpdate: > render: > componentDidUpdate:
- **组件销毁阶段**:一辈子只执行一次
> componentWillUnmount:
组件生命周期的执行顺序:
1. **Mounting:** - constructor() - componentWillMount() - render() - componentDidMount() 2. **Updating:** - componentWillReceiveProps(nextProps) - shouldComponentUpdate(nextProps, nextState) - componentWillUpdate(nextProps, nextState) - render() - componentDidUpdate(prevProps, prevState) 3. **Unmounting:** - componentWillUnmount()
react包中有pureComponent可以继承,那么它跟component有什么差呢?其实就是在shouldComponentUpdate这个生命周期钩子函数里做了一点优化,进行了一次浅对比避免了 不必要的渲染:React PureComponent 学习及浅比较详解
// pureComponent中shouldComponentUpdate源码 if (this._compositeType === CompositeTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } // shallowEqual源码 // 1. hasOwnProperty 判断对象中是否含有指定属性,区别于in,它忽略从原型链上继承的属性 // 2. 下面用的Object.is实际源码中用的是polyfill const shallowEqual=(objA,objB)=>{ const hasOwn=Object.prototype.hasOwnProperty; if (Object.is(objA,objB)) return true; if (typeof (objA)!=='object'||objA===null||typeof (objB)!=='object'||objB===null) return false; const keysA=Object.keys(objA); const keysB=Object.keys(objB); if (keysA.length!==keysB.length) return false; for (let i=0;i<keysA.length;i++){ if (!hasOwn.call(objB,keysA[i])||Object.is(objA[keysA[i]],objB[keysA[i]])) return false; } return true; }shallowEqual 会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是 一个引用,也就是只比较了 第一层 的值,确实很浅,所以深层的嵌套数据是对比不出来的。
React-router使用:
References:
新手理解react-router关键点解析:了解match参数
react router路由传参
开始一个React项目(三)路由基础(v4)
简单使用:
import { BrowserRouter as Router, Route, Link } from 'react-router-dom' react-router和react-router-dom区别:react-router和react-router-dom的区别,推荐使用react-router-dom,react-router-dom引入了一些<Link> <BrowserRouter>这样的dom类组件,更加方便使用两种模式:BrowserRouter即history模式(官网推荐),HashRouter即hash模式,两者可以在引入的时候直接指定<Router></Router>相当于一个盒子,里面只能包含一个子元素。
<Link></Link>标签和<a></a>标签类似,点击<Link></Link>标签中的元素可以实现组件跳转。跳转到哪个组件要看path的值"/"代表根路径,to:path
<Link>和<NavLink>区别:如果仅仅需要匹配路由,使用Link就可以了,而NavLink的不同在于可以给当前选中的路由添加样式, 比如activeStyle和activeClassName
switch使用,如果有重复路由,只匹配第一个路由
路由配置的具体体现:<Route> 配置Route path, component ,exact,strict
Redirect使用,放在switch的最后,用来处理所有都不匹配的情况下的路由处理.React-Router实战:重定向Redirect
路由传参的三种方式以及对比:react router路由传参