一、Vue生命周期
vue生命周期简介
beforecreate :el 和 data 并未初始化
created:完成了 data 数据的初始化,el没有
beforeMount:完成了 el 和 data 初始化
mounted :完成挂载
beforeMountel还是 {{message}},这里就是应用的 Virtual DOM(虚拟Dom)技术,先把坑占住了。到后面mounted挂载的时候再把值渲染进去。
生命周期使用:
beforecreate : 举个栗子:可以在这加个loading事件
created :在这结束loading,还做一些初始化,实现函数自执行
mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy : 你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
二、Vuex应用场景
vuex只能用于单个页面中不同组件(例如兄弟组件)的数据流通。
三、Vue项目
项目路由入口:
router.js文件
1. 事件修饰符
.stop
.prevent
.capture
.self
.once
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
2. 案件修饰符
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
3. v-model修饰符
- .lazy
同步输入转成 change 事件(失焦事件)中同步;
- .number
自动将用户输入值转成Number类型值;
- .trim
自动过滤用户输入的首尾空格;
4. Vue.js 组件
Prop 验证
组件可以为 props 指定验证要求。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
自定义事件
通过使用自定义事件,将子组件数据传递给父组件。
用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
<div id="app">
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
</div>
<script>
Vue.component('button-counter', {
template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
data: function () {
// data 选项是一个函数,组件不相互影响
return {
counter: 0
}
},
methods: {
incrementHandler: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
自定义指令
指令是附着在组件上的某种行为或者功能。
Vue指令:用以改写某个组件的默认行为,或者增强使其获得额外功能,一般来说可以在同一个组件上叠加若干个指令,使其获得多种功能。比如 v-if,它可以安装或者卸载组件。
自定义指令具有生命周期并且生命周期内含有参数。
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
生命周期参数;
el: 指令所绑定的元素,可以用来直接操作 DOM,就是放置指令的那个元素。
binding: 一个对象,里面包含了几个属性,这里不多展开说明,官方文档上都有很详细的描述。
vnode:Vue 编译生成的虚拟节点。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
binding: 一个对象,包含以下属性:
name: 指令名,不包括 v- 前缀。
value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2。
oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression: 绑定值的表达式或变量名。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"。
arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"。
modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。
v-focus
页面载入时,input 元素自动获取焦点:
<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
// 创建根实例
new Vue({
el: '#app'
})
5. Vue.js 路由
1.简单版路由实现步骤:
// 0. 如果使用模块化机制编程,導入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写)相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 现在,应用已经启动了!
2.<router-link>
相关属性
1.to—目标链接
原理:to表示目标路由链接,点击后会立即将to的值传到router.push(),to值可以是字符串或者描述目标位置的对象。
<!-- 字符串 -->
<router-link to="home">Home</router-link>
<!-- 渲染结果 -->
<a href="home">Home</a>
<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'home'">Home</router-link>
<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'home'">Home</router-link>
<router-link :to="{ path: 'home' }">Home</router-link>
<!-- 上述表达式 渲染结果相同 -->
<!-- 命名的路由,下面的结果为 /user/123 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<!-- 对应routes -->
const routes = [{ path: '/user/:userId', name: 'user', component: Bar }]
<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
<!-- 对应routes -->
const routes = [{ path: '/register', component: Foo }]
2.replace—跳转方式
点击触发 router.replace() 而不是 router.push(),导航后不会留下 history 记录
<router-link :to="{ path: '/abc'}" replace></router-link>
3.append—跳转方式
点击后hui在当前相对路由前添加基路径。
例如:从导航/a跳转到/b,若未进行设置,跳转后页面为/b,如果进行设置了,跳转后路径为/a/b
<router-link :to="{ path: 'relative/path'}" append></router-link>
4.tag—渲染元素
在项目中,有时候我们想要将
渲染成某种标签,例如 - ,我们可以在
中添加tag属性并设置类型,改变后的渲染元素依然会监听点击事件,触发导航。
<router-link :to="{ path: '/foo'}" tag="span" append>321321</router-link>
5.active-class—添加链接激活时样式
<style>
._active{
background-color : red;
}
</style>
<p>
<router-link to="/bar" active-class = "_active" >Go to Bar</router-link>
</p>
6.exact-active-class—精确匹配激活样式
<style>
._active{
background-color : red;
}
</style>
<p>
<router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
</p>
7.event—事件声明
声明可以用来触发导航的事件,值可以是一个字符串也可以是一个数组。
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>
<!-- 以上代码设置了mouseover,在鼠标移动到<router-link>标签后内容后会激活链接 -->
6. Vue.js 过渡 & 动画
vue在插入、更新或者移除DOM时,提供不同方式的过渡效果。
Vue提供了内置的过渡封装组件,可以通过包裹组件来实现过渡效果。
过渡类名
如果<transition> 标签没有名字,则 v- 是这些类名的默认前缀。如果你使用了<transition name="my-transition"> ,那么 v-enter 会替换为 my-transition-enter。
v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
自定义过渡的类名:
此类名优先级高于普通的类名,以便与第三方(如:animate.css)的动画库结合使用。
enter-class
enter-active-class
enter-to-class (2.1.8+)
leave-class
leave-active-class
leave-to-class (2.1.8+)
CSS 过渡
与css动画同理,将css过渡样式添加在指定过渡类名中即可。
CSS 动画
animation的使用
过渡和动画同时使用
过渡和动画可以分别理解为 transitionend 或 animationend 。
但是在一些场景中,需要给同一个元素设置两种过渡效果,比如animation很快触发,而transition效果还没结束。这种情况下,需要使用type特性并设置animation或transition来明确需要监听的类型。
显性的过渡持续时间
多数情况,Vue可以自动得出过渡效果的完成时机。
Vue 会等待其在过渡效果的根元素的第一个transitionend
或animationend
事件
显性的过渡持续时间 (以毫秒计):
<transition :duration="1000">...</transition>
进入和移出的持续时间:
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
JavaScript 钩子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 进入中
// --------
beforeEnter: function (el) {
// ...
},
// 此回调函数是可选项的设置
// 与 CSS 结合时使用
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 此回调函数是可选项的设置
// 与 CSS 结合时使用
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
这些钩子可以单独使用,也可以结合CSS transitions/animations 使用。
在 enter
和 leave
中必须使用 done
进行回调。否则,将会被同步调用,过渡会立即完成。
推荐对于仅使用JS过渡的元素添加v-bind:css='false',Vue会跳过CSS的检测。这也可以避免过渡过程中CSS的影响。
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<body>
<div id = "databinding">
<button v-on:click = "show = !show">点我</button>
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
v-bind:css="false"
>
<p v-if="show">菜鸟教程 -- 学的不仅是技术,更是梦想!!!</p>
</transition>
</div>
<script type = "text/javascript">
new Vue({
el: '#databinding',
data: {
show: false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
el.style.transformOrigin = 'left'
},
enter: function (el, done) {
Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
Velocity(el, { fontSize: '1em' }, { complete: done })
},
leave: function (el, done) {
Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
Velocity(el, {
rotateZ: '45deg',
translateY: '30px',
translateX: '30px',
opacity: 0
}, { complete: done })
}
}
})
</script>
初始渲染的过渡
可以通过 appear 特性设置节点在初始渲染的过渡
<transition appear>
<!-- ... -->
</transition>
这里默认和进入/离开过渡一样,同样也可以自定义 CSS 类名。
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" (2.1.8+)
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
自定义 JavaScript 钩子:
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook"
>
<!-- ... -->
</transition>
多个元素的过渡
当设置多个元素过渡时,需要注意的是当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。
<transition>
<!-- <table v-if="items.length > 0"> -->
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>
可以通过给同一个元素的 key
特性设置不同的状态来代替 v-if
和 v-else
,上面的例子可以重写为:
<transition>
<button v-bind:key="isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
</transition>
使用多个 v-if
的多个元素的过渡可以重写为绑定了动态属性的单个元素过渡。例如:
<transition>
<button v-if="docState === 'saved'" key="saved">
Edit
</button>
<button v-if="docState === 'edited'" key="edited">
Save
</button>
<button v-if="docState === 'editing'" key="editing">
Cancel
</button>
</transition>
可以重写为:
<transition>
<button v-bind:key="docState">
{{ buttonMessage }}
</button>
</transition>
// ...
computed: {
buttonMessage: function () {
switch (this.docState) {
case 'saved': return 'Edit'
case 'edited': return 'Save'
case 'editing': return 'Cancel'
}
}
}
7.Vue.js 混入
混入 (mixins)定义了一部分可复用的方法或者计算属性。
混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
<div id = "databinding"></div>
<script type = "text/javascript">
var vm = new Vue({
el: '#databinding',
data: {
},
methods : {
},
});
// 定义一个混入对象
var myMixin = {
created: function () {
this.startmixin()
},
methods: {
startmixin: function () {
document.write("欢迎来到混入实例");
}
}
};
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component();
</script>
选项合并
当组件和混入对象含有同名选项时,选项会进行合并。
数据对象在内部会进行合并(一层属性深度),和组件数据发生冲突时以组件数据优先。
var mixin = {
created: function () {
document.write('混入调用' + '<br>')
}
}
new Vue({
mixins: [mixin],
created: function () {
document.write('组件调用' + '<br>')
}
});
数据结果:
混入调用
组件调用
如果 methods 选项中有相同的函数名,Vue 实例会有更高优先级输出。
如下实例,Vue 实例与混入对象的 methods 选项都包含了相同的函数:
<div id = "databinding"></div>
<script type = "text/javascript">
var mixin = {
methods: {
hellworld: function () {
document.write('HelloWorld 方法' + '<br>');
},
samemethod: function () {
document.write('Mixin:相同方法名' + '<br>');
}
}
};
var vm = new Vue({
mixins: [mixin],
methods: {
start: function () {
document.write('start 方法' + '<br>');
},
samemethod: function () {
document.write('Main:相同方法名' + '<br>');
}
}
});
vm.hellworld();
vm.start();
vm.samemethod();
</script>
输出结果为:
HelloWorld 方法
start 方法
Main:相同方法名
全局混入
除局部混入外,还可以全局混入。
但是注意使用! 一旦使用全局混入对象,将会影响到所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。
<script type = "text/javascript">
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
document.write(myOption)
}
}
})
new Vue({
myOption: '12321'
})
// => "hello!"
</script>
8. Ajax(axios)及vue-resource
以下是一个简单的 Get 请求实例,请求地址是一个简单的 txt 文本:
var vm = new Vue({
el:'#box',
data:{
msg:'Hello World!',
},
methods:{
get:function(){
//发送get请求
this.$http.get('/try/ajax/ajax_info.txt').then(function(res){
document.write(res.body);
},function(){
console.log('请求失败处理');
});
}
}
});
9. 响应接口
Vue可以添加数据动态响应接口。
$watch 属性来实现数据的监听,$watch 必须添加在 Vue 实例之外才能实现正确的响应。
<div id = "app">
<p style = "font-size:25px;">计数器: {{ counter }}</p>
<button @click = "counter++" style = "font-size:25px;">点我</button>
</div>
<script type = "text/javascript">
var vm = new Vue({
el: '#app',
data: {
counter: 1
}
});
vm.$watch('counter', function(nval, oval) {
alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
setTimeout(
function(){
vm.counter += 20;
},10000
);
</script>
Vue 不允许在已经创建的实例上动态添加新的根级响应式属性。
Vue 不能检测到对象属性的添加或删除,最好的方式就是在初始化实例前声明根级响应式属性,哪怕只是一个空值。
如果我们需要在运行过程中实现属性的添加或删除,则可以使用全局 Vue,Vue.set 和 Vue.delete 方法。
Vue.set
Vue.set方法用于设置对象的属性,它可以解决Vue无法检测添加属性的问题:
/**
* @description:设置对象属性
* @param {Object Array} target 目标数据
* @param {String Number} key 对象key值或数组id
* @param {type} value 设置结果
* @return:
*/
Vue.set( target, key, value )
<div id = "app">
<p style = "font-size:25px;">计数器: {{ products.id }}</p>
<button @click = "products.id++" style = "font-size:25px;">点我</button>
</div>
<script type = "text/javascript">
var myproduct = {"id":1, name:"book", "price":"20.00"};
var vm = new Vue({
el: '#app',
data: {
products: myproduct
}
});
vm.products.qty = "1";
console.log(vm);
vm.$watch('products.id', function(nval, oval) {
alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
</script>
在以上实例中,使用以下代码在开始时创建了一个变量 myproduct:
var myproduct = {"id":1, name:"book", "price":"20.00"};
该变量被赋值给Vue实例的data对象。
如果我们想给myproduct对象添加一个或多个属性,我们可以通过如下方法来进行创建:
vm.products.qty = "1";
但是我们通过观察控制台输入结果,可以发现:
$data: Object
products: Object
id: 6
name: "book"
price: "20.00"
qty: "1"
__ob__: eo {value: {…}, dep: Gi, vmCount: 0}
get id: ƒ ()
set id: ƒ (e)
get name: ƒ ()
set name: ƒ (e)
get price: ƒ ()
set price: ƒ (e)
__proto__: Object
虽然我们在products属性中也添加上了qty属性,但是get/set方法只对id、name,price属性有效,并不能用于qty属性中。
我们通过Vue对象来实现响应,Vue主要用在开始时创建所有属性。但是我们可以通过Vue.set来实现:
<script type = "text/javascript">
var myproduct = {"id":1, name:"book", "price":"20.00"};
var vm = new Vue({
el: '#app',
data: {
products: myproduct
}
});
Vue.set(myproduct, 'qty', 1);
console.log(vm);
vm.$watch('products.id', function(nval, oval) {
alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
</script>
控制台输出结果:
products: Object
id: 1
name: "book"
price: "20.00"
qty: 1
__ob__: eo {value: {…}, dep: Gi, vmCount: 0}
get id: ƒ ()
set id: ƒ (e)
get name: ƒ ()
set name: ƒ (e)
get price: ƒ ()
set price: ƒ (e)
get qty: ƒ ()
set qty: ƒ (e)
__proto__: Object
Vue.delete
用于删除动态添加的属性:
/**
* @description:设置对象属性
* @param {Object Array} target 目标数据
* @param {String Number} key 对象key值或数组id
* @return:
*/
Vue.delete( target, key )
eg:
var myproduct = {"id":1, name:"book", "price":"20.00"};
...........
Vue.delete(myproduct, 'price');
console.log(vm);
...........
控制台输出结果:
$data: Object
products: Object
id: 2
name: "book"
__ob__: eo {value: {…}, dep: Gi, vmCount: 0}
get id: ƒ ()
set id: ƒ (e)
get name: ƒ ()
set name: ƒ (e)
__proto__: Object
10. 补充
1.v-show和v-if区别
在视觉效果上两者没有区别,均可以用来通过判断条件来在页面进行元素展示。
然而在实际上通过在浏览器对渲染结果观察可以发现:
1.v-if在进行真假判断时,会根据条件在页面中插入删除标签,在判断为false时,页面中不渲染对性能没有影响,如果判断为true则需要在页面中插入目标元素,我们都知道dom元素的改变会造成回流,消耗浏览器性能,尤其是在页面比较复杂时候。而这正是我们在前端工作中应该要去避免的。
2.v-show经过判断后无论真假,被判断元素在页面中都会进行渲染,即使判断为false时,元素也成功被渲染,只是样式被设置成了display:none;
所以说,在开发过程中,在两种渲染判断都能满足需求时,尽量采用v-show来编写我们的代码。
11. 感谢
最近准备着手vue项目,也刚开始看vue之前一直在用其它框架开发, 欢迎大家提意见,欢迎大家提出一些vue常见问题,可以在这里进行讨论,再将问题添加到文章中,使文章可以更有深度,感谢~