初始配置
$ 1 创建基本的webpack项目
创建目录
npm init -y
创建package.jsoncnpm i webpack webpack-cli -D
安装webpack
webpack-cli
包在根目录创建webpack.config.js并写入相应代码
module.exports={mode:'development'}
终端里输入
webpack
进行打包,之后dist文件夹里多出个main.js,打包成功配置实时打包编译
cnpm i webpack-dev-server -D
package.json
中scripts
里写入"dev": "webpack-dev-server --open --port 3000 --hot --host 127.0.0.1 --progress --compress"
在webpack-dev-server
之后的如--open,--hot等代码是功能性代码,可选择性写
`--open` 设置自动打开默认浏览器 `--oprn chrome`设置自动打开google浏览器
`--port 3000` 设置端口号为3000,若不写则默认是8080
`--host 127.0.0.1`设置域名为127.0.0.1
`--compress` 压缩
`http://loaclhost:8080/`
npm run dev
运行,文件可在http://localhost:8080打开(ctrl+c
可以终止npm run dev
)
将index.html 中<script src="../dist/main.js"></script>
路径改为/main.js
因为webpack-dev-server打包好的main.js是托管到内存中的,在内存中是看不到的,
但却实实在在的存在于根目录中,所以用/main.js引入根目录中的main.js
解决经常访问磁盘目录问题——将index页面也放入内存中即可
cnpm i html-webpack-plugin -D
安装在内存中自动生成html页面的插件,解决每保存一次就调用代码消耗磁盘性能的问题- 在
webpack.config.js
写入如下代码(dirname前是两个 _ )--注意,代码千万不要敲错
const path=require('path')
const HtmlWebPackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebPackPlugin({
template: path.join(__dirname,'./src/index.html'),
filename:'index.html'
})
module.exports = {
mode: 'development',
plugins:[
htmlPlugin
]
}
在这运行之后,系统会自动把打包好的js注入到页面中去(如下图蓝色部分是系统自动注入的)。所以<script src="/main.js"></script>
可以省去了!
- npm run dev即可(注,不要cnpm run dev, cnpm一般只用来装包)
———————————————以上,初步配置完成———————————————
$ 2 react起步(react中,一切都是用js来表现的)
安装react react-dom包
cnpm i react react-dom -S
- 在index.js里导入包
import React from 'react'
import ReactDOM from 'react-dom'
- 接下来可以根据格式来写代码了,在index.js里写
如
创建虚拟DOM元素: const myh1 = React.createElement("h1",{id:"myh1",title:"thish1"},"这是一个h1标签及其内容")
`参数1:标签类型:div,h1,input等:
参数2:标签属性,{attr:"aa",attr:"bb"},不设置就写null`
`参数3:标签子节点,或者说内容
参数n:其他更多子节点`
调用render函数渲染: ReactDOM.render(myh1,document.getElementById("list1"))
`参数1:要渲染的那个虚拟DOM元素,如myh1;
参数2:指定html页面上的一个容器(必须写成dom元素的形式,就是这里的document.getElementById)
如<div id="list1"></div>`
标签嵌套写法
JSX
- React.createElement形式写起来太繁琐
- 于是react开发出了在js中,混合写入类似于html的语法,我们称之为JSX
- 写好之后通过babel转换成React.createElement形式执行便可
- jsx 语法的本质:并不是直接把 jsx 渲染到页面上,而是内部先转换成了 createElement 形式,再渲染的
配置babel
(版本配置问题,避免踩坑从我做起 https://blog.csdn.net/tj310/article/details/88134512 )
- 安装
babel
插件- 运行
cnpm i babel-loader @babel/core @babel/plugin-transform-runtime @babel/runtime -D
- 运行
cnpm i @babel/preset-env @babel/plugin-proposal-class-properties -D
- 运行
- 安装能够识别转换jsx语法的包,就是将jsx转化为js
@babel/preset-react
- 运行
cnpm i @babel/preset-react -D
- 运行
- 在根目录添加
.babelrc
配置文件
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties"]
}
- 在webpack.config.js的module.exports中添加babel-loader配置项:
module: { //要打包的第三方模块
rules: [
{ test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ } //exclude一定不能丢
]
}
- 后缀名的省略及改变路径
在以上,在webpack.config.js之中的module下方添加如下代码
resolve: {
extensions: ['.js', '.jsx', '.json', '.vue'], // 表示,这几个文件的后缀名,可以省略不写
alias: { // 表示别名
'@': path.join(__dirname, './src') // 这样,@ 就表示 项目根目录中 src 的这一层路径
}
}
创建组件(两种方法,本质区别是有无state属性)
方法1:普通function模式创建组件(用的不多)
1、 src目录下创建components文件夹,里面创建组件文件(如comp1.jsx),在里面写入组件
`如`
import React from "react" `*****必须引入React包*****`
function Hello(props) { `***组件名称首字母大写***`
return <div>这是一个Hello组件----{props.name}----{props.age}----{props.weight}</div>
}
export default Hello `*****必须暴露组件*****`
2、在index.js文件里引入组件
import Hello(组件名称) from "./components/comp1.jsx" `*****必须引入组件*****`
const arrStr = ["haha", "heihei", "hehe", "xixi"]
const dog={
name:"金毛",
age:"2",
sex:"公",
weight:"50kg"
}
ReactDOM.render(<div>
<Hello {...dog}></Hello> `***使用组件,以组件做标签名来使用,记住要和组件名一模一样***`
`{...dog}`,es6语法---展开运算符
{arrStr.map(item => < div key = {item.id}><h3>{item}</h3></div > )} //map方法映射数组,箭头函数,key
</div>, document.getElementById("list1"))
方法2:class关键字创建组件
- 先了解什么是class关键字
创建一个动物类
//在class的{}区间内只能写构造器(内部包含实例属性),静态属性,实例方法,静态方法
//class关键字内部,还是原来的js方法来写,只不过更方便了,因此把class关键字称作 语法糖
const Animal { // 这是class关键字
构造器://class关键字内必须要有。每当执行new这个类的时候,会优先执行构造器中的代码
constructor("name","age"){
this.name=name
this.age=age
}
静态属性://在class内部,通过static修饰的属性
static info = "aaaaa"
实例方法(经常用到)
yaoweiba(){
console.log("这是一个实例方法")
}
静态方法(用的不多)
static yaoweiba(){
console.log("这是Animal的静态方法")
}
}
const a1 = new Animal("金毛", 2)
console.log(a1)
a1.yaoweiba() 这是实例方法的调用
Animal.yaoweiba() 这是静态方法的调用
- class关键字的继承
class Person{ //父类
constructor(name,age){ //这里是公用样式
this.name=name
this.age=age
}
sayHello(){
console.log("大家好")
}
}
class American extends Person{ `// class 子类名 extends 父类名`
constructor(name,age){
super(name,age) `//如果写构造器了,则super()必须要有,而且必须放在构造器里的第一行`
}
}
const am1 = new American("jack",20)
console.log(am1)
am1.sayHello()
class Chinese extends Person{
constructor(name,age,IdNumber){
super(name,age)
this.IdNumber = IdNumber
}
}
const ch1 = new Chinese("张三",22,"342422************")
console.log(ch1)
ch1.sayHello()
- class关键字创建组件
// 如果要使用 class 定义组件,必须 让自己的组件,继承自 React.Component
class Person extends React.Component {
constructor() { //给class创建的组件绑定私有数据(普通创建不支持)
super()
// 只有调用了 super() 以后,才能使用 this 关键字
this.state = { // 这个 this.state = {} 就相当于 Vue 中的 data() { return { } }
msg: '大家好,我是 class 创建的 Person组件'
}
}
// 在 组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM结构
render(){
// render 函数中,必须 返回合法的 JSX 虚拟DOM结构
this.state.msg="msg的值被我修改了" //state私有数据的值是可以修改的,而props是不可修改的
return <div>
<h5>这是 class 创建的组件---{this.props.name}---{this.props.age}---{this.props.wife}</h5>
<h2>{this.state.msg}</h2></div>
//在 class 关键字创建的组件中,如果想使用外界传递过来的 props 参数,不需接收,直接通过 this.props.属性名 访问
}
}
const person={
name:"林子闲",
age:22,
wife:"乔韵"
}
ReactDOM.render(<div>
<Person name={person.name} age={person.age} wife={person.wife}></Person>
//必须用组件名以标签的形式调用
<Person {...person}></Person>
</div>,document.getElementById("list1"))
使用CSS样式美化组件
详细教程请看 https://www.bilibili.com/video/av25294556/?p=42 (第42集)
1、先下载相关包
cnpm i style-loader css-loader -D
2、在webpack.config.js内的rules里配置如下代码
{ test: /\.css$/, use: ['style-loader', 'css-loader']}
3、在src文件夹下创建css文件夹,再在里面创建css文件,然后写样式
如 src > css > style1.css
在style1.css里写样式
.mystyle{
color:red;
font-size: 14px;
}
4、在相关文件中引入css文件
import cssobj from "@/css/style1.css" //这里固定写法cssobj,而非css
5、接下来可以在相关文件中通过className给定样式了
如<div className="mystyle">哈哈</div>
CSS模块化——避免css样式污染全局代码问题,让其只在当前区域生效
* css模块化只针对 `类选择器` 和 `id选择器` 生效
1、 将webpack.config.js内的rules里的以下代码
{ test: /\.css$/, use: ['style-loader', 'css-loader']} 改为
{ test: /\.css$/, use: ['style-loader', 'css-loader?modules']}
2、 给定样式的方式改成
如 <div className={cssobj.mystyle} id={css.obj.mystyle2}>哈哈</div>
如果有某些css元素不想被模块化,想全局生效
1、将webpack.config.js内的rules里的一下代码
{ test: /\.css$/, use: ['style-loader', 'css-loader?modules']} 改为
{ test: /\.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]'] }
//[path] 表示样式表 相对于项目根目录 所在路径
//[name] 表示 样式表文件名称
//[local] 表示样式的类名定义名称
//[hash:length] 表示最多32位的hash值,length可以指定具体数值
2、 :global(.allstyle){ //这样写就不会被模块化了 :local(属性名)
font-size: 12px;
padding: 0;
}
3、然后在相关文件里引用
<div className={cssobj.mystyle} className={.allstyle}>哈哈</div>
也可以这么写
<div className={cssobj.mystyle+" allstyle"}>哈哈</div> //allstyle前面有空格哦
也可以这么写
<div className={[cssobj.mystyle,"allstyle"].join(" ")}>哈哈</div> //空格哦
在项目中启用模块化并同时使用bootstrap
原理:第三方的样式表是以.css为后缀名,而我们将自己的样式用scss或less来做后缀名, 这样就可以区分开来自己的样式和第三方样式,从而可以给scss,less启用模块化,而不 影响第三方样式
1、下载bootstrap包 cnpm i bootstrap -S
2、下载包处理字体文件的url包 cnpm i url-loader -D cnpm i file-loader -D
3、webpack.config.js的rules里添加 { test:/\.ttf|woff|woff2|eot|svg$/,use:'url-loader'}
4、下载sass的包 cnpm i sass-loader node-sass -D
5、webpack.config.js的rules里添加 { test:/\.scss$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']}
6、将{ test: /\.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader'] }
改为{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
7、在相应jsx或js文件里引入 import "bootstrap/dist/css/bootstrap.css"
然后尽情发挥吧
webpack.config.js里本阶段rules最终版如下
rules: [
{test: /\.js|jsx$/,use: 'babel-loader',exclude: /node_modules/},
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test:/\.ttf|woff|woff2|eot|svg$/,use:'url-loader'},
{ test:/\.scss$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']}
]
为按钮绑定事件及修改私有数据
onClick onMouseenter onMouseover等 要采用小驼峰命名
import React from "react"
import "bootstrap/dist/css/bootstrap.css"
export default class Hello extends React.Component{
constructor(){
super()
this.state={
msg: '我没被修改呢'
}
}
render(){
/*this.state.msg="我被一this.state.msg的方式修改了"*/
return <div>
<h5>这是一个Hello组件</h5><br/>
<h5>{this.state.msg}</h5>
<button className="btn btn-primary" onClick={ ()=>{this.myclickHandler(参数1,参数2)}} >按钮</button>
//函数调用用这种形式来写,可以不写参数 onClick={()=>{this.myclickHandler()}}
</div>
}
myclickHandler=(a,b)=>{ //函数部分用箭头函数来写,可以不写参数a,b myclickHandler=()=>{ }
alert("我被点击了"+a+b);
this.setState({ //若要修改私有数据state,react给定了一个方法 this.setState({ }),同时记住this.state.msg在这里不可用
msg:'我被以this.setState的方式修改了'
},function(){this.state.msg}) //此处为回调函数,可不加
}
}
文本框与state值之间的双向绑定
import React from 'react'
export default class BindEvent extends React.Component {
constructor() {
super()
this.state = {
msg: '哈哈',
age: 22,
}
}
render() {
return <div>
{/* 需求:点击按钮,把修改 msg 的值 */}
<button onClick={() => this.show('aa', 'bb')}>按钮</button>
<h3>{this.state.msg}</h3>
/* 如果 我们只是把 文本框的 value 属性,绑定到了 state 状态,但是,如果不提供 onChagne 处理函数的话,得到的文本框,将会是一个只读的文本框 */
/* 当为文本框绑定 value 值以后,要么同时提供一个 readOnly, 要么,提供一个 onChange 处理函数 */
/* <input type="text" style={{ width: '100%' }} value={this.state.msg} readOnly /> */
<input type="text" style={{ width: '100%' }} value={this.state.msg} onChange={(e) => this.txtChanged(e)} />
</div>
}
// 每当文本框的内容变化了,必然会调用 这个 txtChanged
txtChanged = (e) => {
// 在 onChange 事件中,获取 文本框的值,通过事件参数 e 来获取
// console.log(e.target.value);
const newVal = e.target.value
this.setState({
msg: newVal
})
}
快速搭建react项目
1、电脑里新建文件夹
2、npm create react-app 项目名称 如 npm create react-app huihuidemo / yarn create react-app huihuidemo
3、cd 项目名称 如 cd huihuidemo
4、npm run start
5、安装antd cnpm i antd -S / yarn add antd
6、在css文件中最顶部引入 @import '~antd/dist/antd.css'; //分号不能丢
(也可在js文件里直接引入, import 'antd/dist/antd.css'; )
7、在js jsx文件中引入需要使用的组件 import {Button,Icon...} from 'antd';
8、(以后必要)安装路由和ajax请求插件 yarn add react-router-dom axios
具体可见antd官网 https://ant.design/docs/react/use-with-create-react-app-cn
解决react脚手架不支持less的问题
1、安装less相关包 cnpm i less less-loader -S / yarn add less less-loader
2、暴露webpack npm run eject / yarn eject (此语句执行后会有config和scripts文件夹生成)
若出现以下错误: (Remove untracked files, stash or commit any changes, and try again.)
是因为我们使用脚手架创建一个项目的时候,自动给我们增加了一个.gitignore文件,而我们本地却没有文件仓库
这样解决: 先输入 git add . 再输入 git commit -m 'up'
3、找到webpack.config.js的const cssRegex = /\.css$/; (第38行) 做出如下改动
- const cssRegex = /\.css$/;
+ const cssRegex = /\.(css|less)$/;
4、找到webpack.config.js的const loaders ,在里面最后一组后面添加
{ loader: require.resolve('less-loader') },
添加之后是这样的:
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {...
},
{...
},
{...
},
{
loader: require.resolve('less-loader')
},
].filter(Boolean);
5、大功告成
ps:若npm start失败,出现这个问题(Build fails after eject: Cannot find module '@babel/plugin-transform-react-jsx' )
则可以
1、删除 node_modules 文件夹
2、运行 yarn
3、cnpm i less less-loader -S / yarn add less less-loader
3、重新 npm start / yarn start