Vue.js详细笔记总结
基本语法
插值表达式
-
使用
v-cloak
能够解决差值表达式闪烁的问题<p v-cloak>{{msg}}</p>
-
使用v-html指令用于输出html代码:
<div v-html = "message"></div> <script> const app = new Vue({ el: '#app', data: { message:'<h1>Hello</h1>' } }); </script>
-
默认 v-text是没有闪烁问题的,它会覆盖元素中原本的内容,但是插值表达式只会替换自己,不会把整个元素的内容清空
<h4 v-text="msg"></h4>
-
v-bind
:属性
绑定机制 简写是:
<input type="button" value="按钮2" :click = "show">
-
v-on: 事件绑定机制 简写是
@
,用于监听DOM事件<input type="button" value="按钮2" @:click = "show">
- 在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的,这是,Vue会默认将浏览器生产的event事件对象作为参数传入到方法。
- 在调用方式,使用
$event
来手动获取浏览器参数的event对象
事件修饰符
<!-- 阻止单击事件继续传播 -->
<a :click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form :submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a :click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form :submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div :click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div :click.self="doThat">...</div>
动态绑定style
<div id="app">
<!-- 第一种绑定方法 -->
<h2 :style="{fontSize: size, backgroundColor: color}">{{message}}</h2>
<!-- 第二种绑定方法 -->
<h2 :style="getStyle()">{{message}}</h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
size: '50px',
color: 'red'
},
methods: {
getStyle: function() {
return {fontSize: this.size, backgroundColor: this.color}
}
}
});
</script>
v-for属性
-
迭代数组
<ul> <li v-for = "(item , i) in sites">{{item.name}}</li> </ul> <script> const app = new Vue({ el: '#app', data: { sites:[ { name: 'Runnoob'}, { name:'Google'}, { name:'Taobao'} ] } }); </script>
-
迭代对象的属性
<div v-for = "(value ,key,index) in user">值是:{{value}}---键是:{{key}}---索引是:{{index}}</div> <script> const app = new Vue({ el: '#app', data: { user:{ id:1, name:'guo', gender:'nan' } } }); </script>
-
迭代数字
<p v-for = "i in 10">这是第{{i}}个p标签</p>
- v-for 迭代数字时,i 值从1开始
- key在使用的时候,必须使用
v-bind属性
绑定的形式,指定key的值
v-if和v-show的区别
特点 | 性能 | |
---|---|---|
v-for | 每次都会重新删除或创建元素 | 切换性能高 |
v-show | 每次不会重新进行DOM的删除和创建操作 | 初始渲染消耗高 |
**总结:**如果元素可能永远不会显示出来被用户看到,则推荐v-if;如果元素涉及到频繁切换,则推荐v-show
双向绑定
结合radio类型
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="sex">女
</label>
<h2>{{sex}}</h2>
<script>
const app = new Vue({
el: '#app',
data: {
sex: '男'
}
})
</script>
- sex里面写上数据时,由于双向绑定,对应的按钮处也会有选中效果,这样可以解决单选按钮的默认问题
结合chechbox类型
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>{{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
<script>
const app = new Vue({
el: '#app',
data: {
isAgree: false //单选框对应的是boolean值
}
})
</script>
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="足球" v-model="hobbies">足球
<h2>爱好是{{hobbies}}</h2>
<script>
const app = new Vue({
el: '#app',
data: {
hobbies: [ ] //多选框对应的是数组
}
})
</script>
结合select类型
<select name="abc" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
</select>
<h2>选择的是:{{fruit}}</h2>
<script>
const app = new Vue({
el: '#app',
data: {
fruit: '香蕉'
}
})
</script>
- 注意:select的v-model是写在select中的!
值绑定
我们前面的value中的值,都是定义在input时直接给定,但是在实际开发中,这些input的值可能是定义在data中的,所以需要通过 v-bind:value动态地给value绑定值
<label v-for="item in allHobbies" :for="item">
<input type="checkbox" :id="item" :value="item" v-model="hobbies">{{item}}
</label>
<h2>选择的是:{{hobbies}}</h2>
<script>
const app = new Vue({
el: '#app',
data: {
hobbies: [ ],
allHobbies: ['篮球', '足球', '乒乓球', '羽毛球']
}
})
</script>
使用修饰符
-
lazy修饰符
- 默认情况下v-model是实时同步输入框的数据的
- lazy修饰符可以让数据在失去焦点或回车时才更新
v-model.lazy = "message"
-
number修饰符
- 默认情况下,在输入框中无论输入的是字母还是数字,都会被当做String类型处理
- 但是如果我们希望处理的是数组类型,那么最好直接将内容当做数字处理
-
trim修饰符
- 如果输入的内容首尾有很多
空格
,可以使用它去除
- 如果输入的内容首尾有很多
常用特性
自定义指令
-
全局指令
//页面刷新后输入框的光标默认为输入状态 <div id="app"> <input type="text" v-focus> </div> <script> Vue.directive('focus', { inserted(el) { el.focus(); } }) const app = new Vue({ el: '#app', data: { }, }); </script>
//用带参数的指令改变元素背景色 <div id="app"> <input type="text" v-color='message'> </div> <script> Vue.directive('color', { bind(el, binding) { //根据指令的参数设置背景色 el.style.backgroundColor = binding.value.color; } }) const app = new Vue({ el: '#app', data: { message: { color: 'blue' } } </script>
-
局部指令
<div id="app"> <input type="text" v-focus> </div> <script> const app = new Vue({ el: '#app', data: { message: { color: 'blue' } }, //局部指令在此处添加 directives: { focus: { inserted(el) { el.focus(); } } } }); </script>
其他指令请点击 : Vue文档--自定义指令
计算属性
<div id="app">
<h2>{{fullName}}</h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Guo'
lastName: 'shuaige'
},
//计算属性在此处添加
computed: {
fullName() {
return this.firstName + this.lastName
}
}
})
</script>
过滤器
-
作用:格式化数据,比如将字符串格式化为首字母大写,将日期格式化为指定的格式等。
-
全局过滤器:
//输入一行小写单词,使第一个字母大写 <div id="app"> <input type="text" v-model="message"> <h2>{{message | upper}}</h2> </div> <script> Vue.filter('upper', function(val) { return val.charAt(0).toUpperCase() + val.slice(1); }) const app = new Vue({ el: '#app', data: { message: '' } }); </script>
-
局部过滤器:
//输入一行小写单词,使第一个字母大写 <div id="app"> <input type="text" v-model="message"> <h2>{{message | upper}}</h2> </div> <script> const app = new Vue({ el: '#app', data: { message: '' }, //局部过滤器在此处添加 filters: { upper(val) { return val.charAt(0).toUpperCase() + val.slice(1); } } }); </script>
生命周期
-
Vue实例的产生过程:
数组更新检测
- 变更方法(修改原有数据)
方法名 | 对应用途 |
---|---|
push() | 向数组末尾添加一个或多个元素,并返回新的长度 |
pop() | 移除列表中的一个元素(默认最后一个元素),并返回该元素的值 |
shift() | 删除数组第一个元素,并返回第一个元素的值 |
unshift() | 把它的参数插入arrayObject头部,并将已经存在的元素顺次移到较高的下标处,以便留出空隙 |
splice() | 删除从index处开始的0个或多个元素,并用参数列表中声明的一个或多个值来替代那些被删除的元素 |
sort() | 对数组的元素进行排序 |
reverse() | 颠倒数组中元素的顺序 |
- 替换数组(生成新的数组)
方法名 | 对应用途 |
---|---|
filter() | 创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素;它不会对空数组进行检测 |
concat() | 用于连接两个或多个数组 |
slice() | 从已有的数组中返回选定的元素,包含从开始到最后(不包含最后)的元素 |
可以用新数组接收调用方法后的数组:
this.info = this.info.slice(0, 2);
修改响应式数据
-
第一种:
Vue.set(app.items, indexOfltem, newValue)
-
第二种:
app.$set(app.items, indexOfltem, newValue)
参数的意义:
- 参数一表示要处理的数组名称
- 参数二表示要处理的数组的索引
- 参数三表示要处理的数组的值
组件化开发
基本使用
- 全局组件注册:
//伪代码
Vue.component(组件名称, {
data: 组件数据,
template: 组件模板内容
})
- 举例:
<div id="app">
<!-- 组件使用 -->
<button-counter></button-counter>
</div>
<script>
// 组件注册
Vue.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<div>
<button @click="count++">点击了{{count}}次</button>
</div>
`
})
const app = new Vue({
el: '#app',
data: {
}
});
</script>
- 局部组件注册:
let ComponentA = {/*...*/}
let ComponentB = {/*...*/}
let ComponentC = {/*...*/}
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB,
'component-c': ComponentC,
}
})
- 举例:
<div id="app">
<hello-world></hello-world>
</div>
<script>
let HelloWorld = {
data() {
return {
msg: '123'
}
},
template: '<div>{{msg}}</div>'
}
const app = new Vue({
el: '#app',
data: {},
components: {
'hello-world': HelloWorld
}
});
</script>
注意:局部组件只能在注册他的父组件中使用
组件间的数据交互
-
父组件向子组件传值(静态):在父组件中,直接通过属性的方式给子组件传值;子组件通过props中的对应属性接收
-
父组件向子组件传值(动态):在父组件内部输入值,在子组件通过动态绑定属性来接收
<div id="app"> <div>{{pmsg}}</div> <!-- 静态 --> <menu-item title='来自父组件的值'></menu-item> <!-- 动态 --> <menu-item :title='ptitle'></menu-item> </div> <script> Vue.component('menu-item', { props: ['title'], data() { return { msg: '子组件本身的数据' } }, template: '<div>{{msg + "---" + title}}</div>' }); const app = new Vue({ el: '#app', data: { pmsg: '父组件中的数据', ptitle: '动态绑定' } }); </script>
-
子组件向父组件传递信息:
-
子组件通过自定义事件向父组件传递信息
<button @click='$emit("enlarge-text", 0.1)'>扩大字体</button>
-
父组件监听子组件的事件
<menu-item @enlarge-text='fontSize += $event'></menu-item>
-
-
非父子组件间传值
-
单独的事件中心管理组件间的通信
let eventHub = new Vue();
-
监听事件与销毁事件
eventHub.$on('add-todo', addTodo); eventHub.$off('add-todo');
-
触发事件
eventHub.$emit('add-todo', id);
-
动态组件
-
在 动态组件上使用
keep-alive
//选中一篇文章,切换到另个标签,再切换回来后,让页面停留在之前选择的文章,而不是默认文章,这时可以使用< keep-alive >标签包裹住动态组件 <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive>
插槽
-
基本用法
<div id="app"> //插槽的内容就是在标签中间添加的内容 <alert-box>有BUG</alert-box> </div> <script> Vue.component('alert-box', { template: ` <div> <strong>ERROR:</strong> <slot></slot> </div>` }); const app = new Vue({ el: '#app', data: { } }); </script>
-
具名插槽
<div id="app"> <alert-box> <p slot="header">标题信息</p> <p>内容1</p> <p>内容2</p> <p slot="footer">底部信息</p> </alert-box> </div> <script> Vue.component('alert-box', { template: ` <div> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>` }); const app = new Vue({ el: '#app', data: { } }); </script>
-
作用域插槽
- 应用场景:父组件对子组件的内容进行加工处理
<div id="app"> <cpn></cpn> <cpn> <template slot-scope="slot"> <span v-for="item in slot.data">{{item}}--- </span> </template> </cpn> </div> <template id="cpn"> <div> <slot :data="pLanguages"> <ul> <li v-for="item in pLanguages">{{item}}</li> </ul> </slot> </div> </template>
前后端交互
Promise用法
//实例化Promise对象,构造函数中传递函数,该函数用于处理异步任务
let p = new Promise(function(resolve, reject) {
//成功调用
resolve();
//失败调用
reject();
})
//resolve和reject用于处理成功和失败两种情况,并用p.then获取处理结果
p.then(function(ret) {
//从resolve得到正常结果
}), function(ret) {
//从reject得到错误信息
}
-
常用API:
-
实例方法
- p.then()得到异步任务的正确结果
- p.catch()获取异常信息
- p.finally()成功与否都会执行
queryData() .then(function(data) { console.log(data); }) .catch(function(data) { console.log(data); }) .finally(function() { console.log('finished'); })
-
对象方法
- Promise.all() 并发处理多个异步任务,所有任务都执行完成才能得到结果
- Promise.race() 并发处理多个异步任务,只要有一个任务完成就你得到结果
Promise.all([p1, p2, p3]).then((result) => { console.log(result); }) Promise.race([p1, p2, p3]).then((result) => { console.log(result); })
-
fetch用法
fetch(url).then(fn2)
.then(fn3)
...
.catch(fn)
-
请求参数--常用配置选项:
- method(String): HTTP请求方法,默认为GET(GET、POST、PUT、DELETE)
- body(String):HTTP的请求参数
- headers(Object):HTTP的请求头,默认为{}
fetch('/abc', { method: 'get' }).then(data => { return data.text(); }).then(ret => { //注意这里得到的才是最终的数据 console.log(ret); })
-
GET请求方式的参数传递
//第一种方式 fetch('/abc?id=123').then(data => { return data.text(); }).then(ret => { //注意这里得到的才是最终的数据 console.log(ret); }) //第二种方式 fetch('/abc/123', { method: 'get' }).then(data => { return data.text(); }).then(ret => { //注意这里得到的才是最终的数据 console.log(ret); })
-
DELETE请求方式的参数传递
fetch('/abc/123', { method: 'delete' }).then(data => { return data.text(); }).then(ret => { //注意这里得到的才是最终的数据 console.log(ret); })
-
POST请求方式的参数传递
fetch('/books', { method: 'post', body: 'uname=lisi&pwd=123', header: { 'Content-Type': 'application/x-ww-form-urlencoded' } }).then(data => { return data.text(); }).then(ret => { console.log(ret); })
-
PUT请求方式的参数传递
fetch('/books/123', { method: 'put', body: JSON.stringify({ uname: 'lisi', age: 12 }), headers: { 'Content-Type': 'application/json' } }).then(data => { return data.text(); }).then(ret => { console.log(ret); })
-
fetch响应结果
- text(): 将返回体处理成字符串类型
- json(): 返回结果和JSON.parse(responseText)一样
fetch('/abc' then(data => { return data.json(); }).then(ret => { console.log(ret); })
前端工程化
-
ES6模块化的基本语法
//默认导出语法 export default 默认导出的成员 //默认导入语法 import 接收名称 from '模块标识符'
-
注意:每个模块中,只允许使用唯一的一次 export default ,否则会报错!
-
按需导出和按需导入
//导入模块成员 import { s1, s2 as ss2, say } from './m1.js' //向外导出变量s2 export let s2 = 'ccc' //向外导出 say 方法 export function say = function() {}
-
直接导入并执行模块代码
//有时候,我们只想单纯执行某个模块中的代码,并不需要得到模块中向外暴露的成员 //当前文件为 m2.js for(let i = 0;i < 3;i++) { console.log(i) } //直接导入并执行模块代码 import './m2.js'
路由
基本概念
-
前端路由:负责事件监听,触发事件后,通过事件函数渲染不同的内容;本质上就是用户事件和事件处理函数之间的对应关系
-
后端路由:根据不同的用户URL地址请求,返回不同的内容
-
Vue Router的功能有:
-
支持HTML5历史模式或hash模式
-
支持嵌套路由
-
支持路由参数
-
支持编程式路由
-
支持命名路由
-
Vue-router的基本使用
步骤:
- 第一步:引入相关的库文件
<!-- 导入 vue-router 文件,为全局 window 对象挂载 VueRouter 构造函数 --> <script src="../lib/vue-router.js"></script>
-
第二步:添加路由链接
- router-link是vue中提供的标签,默认会被渲染为a标签
- to属性默认会被渲染为href属性
- to属性的值默认会被渲染为 #开头的hash地址
<router-link to="/user">User</router-link> <router-link to="/register">Register</router-link>
-
第三步:添加路由填充位
- 将来通过路由规则匹配到的组件,将会被渲染到 router-view 所在的位置
<router-view></router-view>
-
第四步:定义路由组件
let User = { template: '<h1>User</h1>' } let Register = { template: '<h1>Register</h1>' }
-
第五步:配置路由规则并创建路由实例
//创建路由实例对象 const router = new VueRouter({ //所有路由规则 routes: [ //注意:component的值只能是对象,不能是字符串 { path: '/user', component: User}, { path: '/register', component: Register} ] })
-
第六步:把路由挂载到Vue根实例中
const app = new Vue({ el: '#app', router: router //也可以简写为router });
路由重定向
路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。而通过redirect属性,指定一个新的路由地址,就可以方便地设置路由的重定向
const router = new VueRouter({ routes: [ //path表示需要被重定向的原地址,redirect指向的是新地址 { path: '/', redirect: '/user'}, { path: '/user', component: User}, { path: '/register', component: Register} ] })
vue-router嵌套路由
使用步骤:父路由组件的步骤和基本的一样,子路由组件需要在他们的父路由组件中定义
//定义两个子路由 let Tab1 = { template: '<h3>Tab1 子组件' } let Tab2 = { template: '<h3>Tab2 子组件' } const router = new VueRouter({ routes: [ //注意:component的值只能是对象,不能是字符串 { path: '/', redirect: '/user'}, { path: '/user', component: User}, { path: '/register', component: Register, children: [ { path: '/register/tab1', component: Tab1}, { path: '/register/tab2', component: Tab2} ] } ] })
vue-router动态匹配路由
应用场景:通过动态路由参数的模式进行路由匹配
<router-link to="/user/1">User 1</router-link> <router-link to="/user/2">User 2</router-link> <router-link to="/user/3">User 3</router-link> let router = new VueRouter({ routes: [ //动态路径参数以 :为开头 { path: '/user/:id', component: User} ] }) const User = { //路由组件通过 $route.params 获取路由参数 template: '<div>User {{ $route.params.id}}</div>' }
但是,$route与对应路由形成高度耦合,不够灵活,所以可以使用props将组件和路由关联
- props的值为布尔类型:
const router = new VueRouter({ routes: [ //props设置为true,则route.params将会被设置为组件属性 { path: '/user/:id', component: User, props: true} ] }) const User = { props: ['id'], //使用props接收路由参数 template: '<div>ID为:{{ id }}</div>' //使用路由参数 }
- props的值为对象类型:
//这种类型的弊端是 id 没有使用到 let User = { props: ['id', 'uname', 'age'], template: '<div>id为:{{id}}--姓名:{{uname}}--年龄:{{age}}</div>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, props: { uname: 'lisi', age: 18}} ] })
- props的值为函数类型:
let User = { props: ['id', 'uname', 'age'], template: '<h1>id为:{{id}}--姓名:{{uname}}--年龄:{{age}}</h1>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, props: route => ({ uname: 'zhangsan', age: 19, id: route.params.id }) } ] })
命名路由
<router-link :to="{ name: 'user', params: {id: 3}}">User 3</router-link> const router = new VueRouter({ routes: [ { name: 'user', path: '/user/:id', component: User, props: route => ({ uname: 'zhangsan', age: 19, id: route.params.id }) } ] })
vur-router编程式导航
-
常用的编程式导航API:
- this.$router.push(‘hash地址’)
- this.$router.go(n) n为正数表示前进,n为负数表示后退
-
针对router.push()方法的参数规则
//字符串(路径名) router.push('/home') //对象 router.push({ path: '/home'}) //命名路由(传参) router.push({ name: '/user', params: { userId: 123 }}) //带查询参数,变成 /register?uname=lisi router.push({ path: '/register', query: { uname: 'lisi' }})
-
用法:
const User = { template: ` <div> <button @click="goRegister">跳转到注册页面</button> </div>`, methods: { goRegister() { this.$router.push('/register') } }
webpack
基本使用
步骤:
-
运行命令:
npm install webpack webpack-cli -D
-
在项目根目录下创建名为 webpack.config.js 的配置文件
-
在webpack的配置文件中,初始化如下配置:
module.exports = { mode: 'development' //mode指定构建模式 }
-
在 package.json 的scripts节点下,新增 dev 脚本:
"scripts": { "dev": "webpack" //script节点下的脚本,可以通过npm run运行 }
-
在终端运行下面代码进行项目打包:
npm run dev
webpack其他使用
-
webpack在打包一个文件时,会自动识别是否依赖其他文件,若依赖有,则自动处理那个文件
-
webpack主要用于处理用户写的js代码,并且webpack会自动处理js之间相关的依赖,但是在开发中我们不仅仅有基本的js代码处理,还需要加载css、图片等等,对于webpack本身来说,这些转化是不支持的。
则需要给webpack扩展对应的
loader
-
loader的使用过程:
- 步骤一:通过npm安装需要使用的loader
- 步骤二:在webpack.config.js中的modules关键字下进行配置
-
css-loader 解析css文件后,使用import加载,并且返回css代码
npm install --save-dev css-loader
-
style-loader 将模块的导出作为样式添加到DOM中
npm install style-loader --save-dev
-
webpack在打包js文件时,ES6语法并没有转成ES5,那么久意味着一些对ES6不支持的浏览器没办法运行程序。
如果希望将ES6语法转成ES5,需要使用
babel
npm install --save-dev babel-loader@ 7bable-core babel-preset-es2015
-
webpack引入vue.js
npm install vue --save
-
el和template的区别
-
在实际开发中,并不希望修改Html模板的代码,可以使用template属性
new Vue({ el: '#app', template: ` <div id="app"> {{message}} </div> `, data: {} })
-
上面的template属性中的代码可以代替Html中的
<div id="app"> <h2>{{message}}</h2> </div>
-
-
plugin是插件的意思,通常是用于对某个现有的架构进行扩展
- loader和plugin的区别
- loader主要是用于转换某些类型的模块,它是一个转换器
- plugin是插件,它是对webpack本身的扩展,是一个扩展器
- 使用步骤:
-
步骤一:通过npm安装需要使用的plugins
-
步骤二:在webpack.config.js中的plugins中配置插件
-
- loader和plugin的区别
Vue单文件组件
使用Vue单文件组件,每个单文件组件的后缀名都是 .vue 每一个Vue单文件组件都由三部分组成:
- template组件组成的模板区
- script组成的业务逻辑区域
- style样式区域
<template> <div>abc</div> </template> <script> export default { data() { return {}; }, methods: {} } </script> // style样式的scoped指令可以防止组件之间的样式冲突 <style scoped> div { color: red; } </style>
Vue脚手架
基本用法
//1.基于交互式命令行的方式,创建vue项目 vue create my-project //2.基于图形化界面的方式,创建vue项目 vue ui
Vue脚手架自定义配置
-
通过package.json配置项目(不推荐使用)
"vue": { "devServer": { "port": 8888, //设置端口号 "open": true //自动打开项目 } }
-
项目根目录创建 vue.config.js(推荐)
module.exports = { devServer: { port: 8888, open: true } }
最后
如果有帮助到你,请给这篇文章点个赞和收藏吧,让更多的人看到 ~ ("▔□▔)/
如果有异议的地方,欢迎在评论区留言,我都会逐一回复 ~
如果感兴趣的话,欢迎访问 眼里要有光的博客