SegmentFault 白话前端最新的文章
2020-05-31T18:12:08+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
我是怎么学会vue的17:父子组件的通信
https://segmentfault.com/a/1190000022796494
2020-05-31T18:12:08+08:00
2020-05-31T18:12:08+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>父组件向子组件传递数据:props(父传子通过属性名传)</h2>
<p>常用于请求列表数据:要显示ul->li列表时,父组件向服务器请求数据,数据存放在父组件里,并把数据传递给子组件,然后子组件使用 v-for 遍历显示。(因为子组件不能直接访问父组件或vue实例的数据)</p>
<h3>传递方法</h3>
<ol>
<li>父组件data定义数据</li>
<li>在子组件的HTML标签中,使用 v-bind 自定义属性,并把父组件数据的值赋给属性</li>
<li>
<p>注册组件时,添加 props 属性,指定传入数据的类型、默认值、是否必须。</p>
<blockquote>注意:<br>1.type支持的类型有:String、Number、Boolean、Array、Object、Date、Function、Symbol<br>2、默认值:认值必须是一个函数<br>3、props在子组件中有多种定义方式:既可以使用数组,也可以使用对象</blockquote>
<pre><code class="js">props写法1:传数组
props:['cmovies','cmessages'] //cmovies、cmessages是变量名</code></pre>
<pre><code class="js">props写法2:传对象
传对象的好处:可以给变量指定类型、提供默认值
props:{
cmovies:{
type:Array,
//default:"aaaaa",
//类型是对象或数组时,默认值必须是一个函数。2.5.17以下的低版本可以写成default:[]
default(){
return []
},
required:true
}
}</code></pre>
</li>
<li>在子组件的模板中使用数据。</li>
</ol>
<blockquote>属性如果不是通过v-bind绑定的,则绑定的是字符串,不是变量;vue不会去父组件里找对应的变量给属性赋值。</blockquote>
<p>当我们有自定义构造函数时,验证也支持自定义的类型。<br><img src="/img/bVbHOyp" alt="image.png" title="image.png"></p>
<h3>props的驼峰标识</h3>
<p>如果props里面的属性名需要使用驼峰标识命名,则组件标签绑定的属性名,需要使用减号分隔的形式命名。</p>
<pre><code class="html"><cpn :c-info="info" :child-my-message="message" v-bind:class></cpn></code></pre>
<pre><code class="js">props: {
cInfo: {
type: Object,
default() {
return {}
}
}</code></pre>
<p>组件模板中用到组件的地方也需要使用驼峰写法。</p>
<pre><code class="html"><template id="cpn">
<div>
<h2>{{cInfo}}</h2>
</div>
</template></code></pre>
<h2>子组件向父组件传递数据:通过自定义事件</h2>
<p>使用情景:子组件点击了,需要告诉父组件 子组件发生了什么事件(点击事件),点击的是哪个。父组件根据我点击的不同内容去请求不同的数据。</p>
<h3>步骤</h3>
<ol>
<li>
<p>子组件发射自定义事件</p>
<ul>
<li>子组件模板 绑定点击事件(或其他事件)</li>
<li>定义点击事件的回调函数:发射事件(自定义事件的事件名、参数)</li>
</ul>
</li>
<li>
<p>父组件监听自定义事件</p>
<ul>
<li>在父组件模板里面绑定自定义事件</li>
<li>定义回调函数,用于接收传递的数据,并根据这个数据执行相应的动作。</li>
</ul>
</li>
</ol>
<h3>演示</h3>
<pre><code class="html"><!--父组件模板-->
<div id="app">
<!--
step2 在父组件模板里 绑定自定义事件。
这里会默认把参数item传进去,不需要写。
-->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<!--step1 子组件模板 绑定点击事件 把参数item传进去-->
<button v-for="item in categories"
@click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'},
{id: 'ddd', name: '电脑办公'},
]
}
},
methods: {
btnClick(item) {
// step1 发射事件: 自定义事件
this.$emit('item-click', item)
}
}
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn
},
methods: {
cpnClick(item) {
//step2 定义回调函数,用于接收传递的数据,并根据这个数据执行相应的动作
console.log('cpnClick', item);
}
}
})
</script>
</html></code></pre>
<h3>注意</h3>
<p>自定义事件的名称,不能使用驼峰写法!</p>
<p>比如这里我们自定义事件的命名,可以写itemclick,也可以写item-click,但是不能写itemClick,否则会报错。</p>
<p>如果是在脚手架里面则可以使用驼峰写法。</p>
我是怎么学会vue的16:为什么组件的data必须是函数
https://segmentfault.com/a/1190000022679486
2020-05-19T00:36:55+08:00
2020-05-19T00:36:55+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>组件的数据定义在哪里</h2>
<p>之前组件的数据都是写死在标签里的,现在我希望组件的数据可以动态变化:数据改变,组件的显示也随着变化。</p>
<p>组件的数据在哪里定义呢?</p>
<p>注册组件时,<strong>数据写在组件自己的data属性中</strong>。以全局组件为例,data是在注册组件的时候来指定的:</p>
<pre><code class="html"><div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册一个全局组件
Vue.component('cpn', {
template: '#cpn',
data() {
return {
title: 'abc' //全局组件的数据
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊' //vue实例的数据
}
})
</script></code></pre>
<p>为什么组件的数据需要保存在组件中,而不是保存在实例中?</p>
<p>因为一个实例有非常多的组件,如果所有的数据都放在vue实例中,那么vue的实例会非常臃肿,所有vue组件内部应该有自己保存数据的地方:保存在data属性中。</p>
<p>data属性必须是一个函数,且这个函数必须返回一个对象,对象内部保存数据。</p>
<h2>为什么组件的data属性必须是函数?</h2>
<p><strong>案例</strong>:之前的计数器案例,把计数器封装成一个组件,用到的时候只需要引用标签即可。</p>
<pre><code class="html"><!--组件实例对象-->
<div id="app">
<cpn></cpn> <!--一个cpn组件即是一个计数器-->
<cpn></cpn> <!--创建了三个计数器-->
<cpn></cpn> <!--思考:这三个组件实例对象,用的是否是同一个data对象?-->
</div>
<template id="cpn">
<div>
<h2>当前计数: {{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.注册组件
Vue.component('cpn', {
template: '#cpn',
data() {
return {
counter:0
}
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script></code></pre>
<p>解析:<br>三个组件,用的是否是同一个data对象?</p>
<p>不是。<br>因为data是一个函数,每次创建组件都会调data函数,且每次调用data函数的时候都会<strong>return一个新的对象</strong>。</p>
<p><code>data(){ }</code>是<code>function data(){ }</code>的简写。</p>
<p>这里的三个组件,创建的时候调用了三次data函数,每次都return了一个新数据。这样三个数据分别占用了三个内存地址,互相就不影响了。</p>
<p>如果想让他们互相影响,应该怎么写呢?</p>
<pre><code class="js"> const obj = {
counter: 0
}
Vue.component('cpn', {
template: '#cpn',
data() {
return obj
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})</code></pre>
<p>这样写的话,三个组件的数据指向的是同一个内存地址,修改其中一个组件,其他两个也会跟着变化。</p>
我是怎么学会vue的15:全局组件vs局部组件、父组件vs子组件
https://segmentfault.com/a/1190000022612589
2020-05-11T22:59:06+08:00
2020-05-11T22:59:06+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>全局组件vs局部组件</h2>
<h2>父组件vs子组件</h2>
<p>父组件通过全局的方式注册。<br>子组件是在某个组件里面注册(不是在全局注册,也不是Vue实例下面注册)。</p>
<p>代码演示:</p>
<pre><code class="html"><div id="app">
<cpn2></cpn2>
<!--<cpn1></cpn1>-->
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是子组件</h2>
<p>我是内容</p>
</div>
`
})
// 2.创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是父组件</h2>
<p>我是内容</p>
<!--使用子组件-->
<cpn1></cpn1>
</div>
`,
components: {
//子组件cpn1注册在其他组件里面
cpn1: cpnC1
}
})
const app = new Vue({
el: '#app',
components: {
cpn2: cpnC2
}
})
</script></code></pre>
<h2>【局部组件】和【子组件】的区别</h2>
<p>局部组件是在Vue实例下面注册,子组件是在其他组件里面注册</p>
我是怎么学会vue的14:组件的基本使用
https://segmentfault.com/a/1190000022602135
2020-05-11T02:07:34+08:00
2020-05-11T02:07:34+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>组件化,即把整个页面拆分成一个个组件,开发一个个独立、可复用的小组件,即可构建起一个应用。这样可以让代码更方便的组织和管理,扩展性也更强。</p>
<p>开发出来的应用会形成一个<strong>组件树</strong>(树结构是一种数据结构)。</p>
<blockquote>组件化与模块化不同,后面会讲到模块化,并对比组件和模块的区别。</blockquote>
<h2>封装组件的步骤</h2>
<ol>
<li>
<strong>创建组件构造器</strong> Vue.extend() 需要传入一个对象<p>Vue.extend创建的是一个组件构造器,通常在创建构造器时,传入template作为我们自定义组件的模板。</p>
<p>Vue.component这种写法在2.X文档中几乎看不到了,一般使用语法糖的形势来写。见下文。</p>
</li>
<li>
<p><strong>注册组件</strong>(全局注册、局部注册)</p>
<pre><code class="js">Vue.component(注册组件的标签名,组件构造器)</code></pre>
<p>Vue.component()是把刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称,所以需要传递2个参数。</p>
</li>
<li>
<strong>使用组件</strong>(在Vue实例的作用域之内来使用组件)</li>
</ol>
<p>演示:</p>
<pre><code class="html"><div id="app">
<!--3.使用组件 需要在实例内使用-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象 传入的是一个对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
}
})
</script></code></pre>
<h2>全局组件和局部组件</h2>
<p>封装组件的第二步是注册组件,在全局下注册的就是全局组件,在某个实例中注册的就是局部组件。</p>
<p>真实开发中用局部组件的比较多。</p>
<h3>全局组件</h3>
<p>全局组件可以在多个vue实例里面使用。但是真实开发中一般只有一个vue实例,所以全局组件用的比较少。</p>
<pre><code class="html"><div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script>
// 1.创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
`
})
const app = new Vue({
el: '#app',
})
const app2 = new Vue({
el: '#app2'
})
// 2.注册组件(全局组件, 意味着可以在多个Vue的实例下面使用)
//这个组件注册在全局下面,所以是全局组件
Vue.component('cpn', cpnC)
</script></code></pre>
<h3>局部组件</h3>
<p>注册在Vue实例下面的,就是局部组件。局部组件只能在注册的那个实例里面使用。</p>
<p>局部组件注册方法:</p>
<pre><code class="js">const app = new Vue({
el: '#app',
components: {
// 使用组件时的标签名,组件构造器
cpn: cpnC
}
})</code></pre>
<p>代码演示:</p>
<pre><code class="html"><div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<script>
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
`
})
const app = new Vue({
el: '#app',
components: {
// cpn使用组件时的标签名
cpn: cpnC
}
})
</script></code></pre>
<h2>使用语法糖注册组件</h2>
<p>前面说到<code>Vue.extend</code>这种写法已经很少见了,现在都使用语法糖的写法。</p>
<h3>使用语法糖注册全局组件</h3>
<pre><code class="html"><div id="app">
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1', {
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
`
})
const app = new Vue({
el: '#app'
})
</script></code></pre>
<h3>使用语法糖注册局部组件</h3>
<pre><code class="html"><div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
components: {
'cpn2': {
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容</p>
</div>
`
}
}
})
</script></code></pre>
<h2>使用模板分离的写法注册组件</h2>
<p>上面演示中,在template里面写了很多html标签,看起来很乱;我们需要把这些HTML标签分离出来,有2种分离的写法。</p>
<h3>写法1:使用script标签(不常用)</h3>
<ul>
<li>把html标签放到script标签里面</li>
<li>注意:script的类型必须是text/x-template,并加上id属性</li>
<li>最后挂载到对应的组件上<code>template: '#cpn'</code>
</li>
</ul>
<pre><code class="html"><div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
</script>
<script src="../js/vue.js"></script>
<script>
// 注册一个全局组件
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app'
})
</script></code></pre>
<h3>写法2:使用template标签(常用)</h3>
<ul>
<li>把标签放到template标签里面,并加上id属性</li>
<li>再挂载到对应的组件上<code>template: '#cpn'</code>
</li>
</ul>
<pre><code class="html"><div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册一个全局组件
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app'
})
</script></code></pre>
我是怎么学会vue的13:v-model 表单数据双向绑定
https://segmentfault.com/a/1190000022586214
2020-05-09T01:35:34+08:00
2020-05-09T01:35:34+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<p>什么是双向绑定?<br>data里是什么,input就显示什么,修改input里的数据,会同步修改data里面对应的数据。</p>
<h2>1.v-model结合type类型使用</h2>
<pre><code class="html"><div id="app">
<input type="text" v-model="message">
</div></code></pre>
<p>input的数据,和data里面的message,是双向绑定了的。</p>
<h2>2.v-model结合radio类型使用</h2>
<pre><code class="html"><div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
</div></code></pre>
<p>此时,data的sex属性里面,即存储了sex的值。</p>
<p>radio单选框如何实现单选(排它),有2种方法:</p>
<ol>
<li>多个radio添加相同name属性<code>name='sex'</code>
</li>
<li>多个radio添加相同v-model属性<code>v-model='sex'</code>
</li>
</ol>
<h2>3.v-model结合checkbox类型使用</h2>
<p>checkbox可以用作单选框,也可以用作多选框。</p>
<h3>3.1 checkbox用作单选框</h3>
<blockquote>checkbox用作单选框,一般用于“用户协议”的勾选,用户选上了之后可以取消勾选(使用单选框的话,用户选中了就没法取消了)。</blockquote>
<pre><code class="html"><label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label></code></pre>
<p>数据:data里存的isAgree是布尔值,true为勾选,false为不勾选。</p>
<h3>3.2 checkbox用作多选框</h3>
<pre><code class="html"><input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球</code></pre>
<p>数据:在data里面新建一个数组<code>hobbies</code>,会自动存放这些数据的value值。</p>
<h2>4.v-model结合select类型使用(不常用)</h2>
<h3>4.1 单选</h3>
<pre><code class="html"><select name="abc" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select></code></pre>
<p>数据:</p>
<pre><code class="js">data: {
fruit: '香蕉' //设置了一个默认值,如果不设置次数留空
}</code></pre>
<h3>4.2 多选(添加上multiple属性)(使用Ctrl选择多个)</h3>
<pre><code class="html"><select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select></code></pre>
<p>数据:</p>
<pre><code class="js">data: {
fruits: []
}</code></pre>
<h2>5.input值的绑定</h2>
<p>有时表单要显示什么,以及表单元素的value值,不是写死的,而是根据服务器传的数据动态显示的,此时就需要给input绑定值。</p>
<p>举例:这段代码的数据是写死的,但是实际开发中,要显示什么,要根据服务器传入的数据决定,所以这里就需要动态显示。</p>
<pre><code class="html"><input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球</code></pre>
<p>动态显示的版本(id也可以动态绑定):</p>
<pre><code class="js"><label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label></code></pre>
<p>数据:</p>
<pre><code class="js">data: {
hobbies: [], // 多选框,
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}</code></pre>
<h2>6.v-model的修饰符</h2>
<h3>6.1 lazy 懒加载</h3>
<p>懒加载可以实现“用到的时候才加载”的效果。</p>
<blockquote>用途:v-model是双向绑定的,比如input,我输入一个字符,马上就会同步到data中。但这样的更新频率太高了,我希望在用户输入完,敲击回车或者input失去焦点,再把用户输入的数据同步到data。</blockquote>
<pre><code class="html"><input type="text" v-model.lazy="message"></code></pre>
<h3>6.2 number 限制只能输入数字</h3>
<pre><code class="html"><input type="number" v-model.number="age"></code></pre>
<p>如果不加<code>.number</code>,即使用户输入的是数字,vue在data里存的数据类型也会是字符串。加上之后,data里存的数据类型就是 number 了。</p>
<h3>6.3 trim 去掉字符串两边的空格</h3>
<pre><code class="html"><input type="text" v-model.trim="name"></code></pre>
我是怎么学会vue的12:数组的哪些方法是响应式的
https://segmentfault.com/a/1190000022537793
2020-05-03T21:28:48+08:00
2020-05-03T21:28:48+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>为什么数组变化,页面会自动刷新?</p>
<p>原因是:数据是响应式的,vue 监听到数据内部发生变化,会自动根据新数据,重新渲染 DOM,再根据虚拟 DOM 修改真实 DOM。</p>
<p>需要注意的是,并不是数据只要变动,页面就跟着变化。</p>
<p>用哪些方式去修改数组,数据是响应式的?</p>
<p>响应式修改数组的方法有:</p>
<ul>
<li>push()</li>
<li>splice()</li>
<li>pop()</li>
<li>shift()</li>
<li>unshift()</li>
<li>sort()</li>
<li>reverse()</li>
</ul>
我是怎么学会vue的11:v-for绑定和不绑定key的区别
https://segmentfault.com/a/1190000022531684
2020-05-02T14:56:04+08:00
2020-05-02T14:56:04+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<p><strong>先说结论:使用v-for时,需要给元素或组件添加key属性。</strong></p>
<h2>加上key的目的:避免重复渲染</h2>
<p>通过案例讲解。</p>
<h3>案例:在字母列表 B 和 C 之间,插入 F</h3>
<pre><code class="html"><div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
letters: ['A', 'B', 'C', 'D', 'E']
}
})
</script></code></pre>
<h4>实现方法</h4>
<p>插入F方法:使用 splice</p>
<pre><code class="js">//浏览器运行代码后,在控制台输入
app.letters.splice(2,0,"F")</code></pre>
<h4>性能上的问题</h4>
<p>先引入一个概念:diff算法。</p>
<blockquote>vue会先把元素放到虚拟dom里,等代码段执行完(比如一个for循环执行完),vue会对比虚拟dom和真实dom的差别,然后只把不同的地方渲染到页面,这样就提高了性能。</blockquote>
<p>在这个插入字母的案例中,for循环执行完了,ABCDE被渲染到了页面上,像下面这样:<br><img src="/img/bVbGIh9" alt="image.png" title="image.png"><br>然后,我们在控制台输入<code>app.letters.splice(2,0,"F")</code>,改变了data,vue就会重新把输入都放入虚拟dom,再把虚拟dom和真实dom比对,只修改不同的地方,系统的地方不做修改。</p>
<p>但问题是:vue比对的时候发现,只有AB是一样的,别的都不一样。所以AB没动,别的都是被重新渲染到页面的。</p>
<p>这种方式并不高效。我们插入一个<code><li>F</li></code>,是希望重新渲染的时候,只添加一个 F 即可,而不是把 FCDE 都替换。</p>
<h4>解决方案</h4>
<p>给节点增加key属性,来制作一个唯一的标识。</p>
<pre><code class="html"><li v-for="item in letters" key="item">{{item}}</li></code></pre>
<p>这个key的值,不能随意添加,<strong>必须要保证key和之后要展示的元素是一一对应的</strong>。</p>
<p>如果我们的key的值使用index:</p>
<pre><code class="html"><li v-for="item in letters" key="index">{{item}}</li></code></pre>
<p>会产生如下的问题:<br><img src="/img/bVbGIkx" alt="image.png" title="image.png"><br>同一个元素C,数据修改前C的index是2,修改后变成了3,使index和item不是一一对应的了。</p>
<p><strong>必须要保证key和之后要展示的item是一一对应的</strong>。</p>
<p>所以这里的key,我们使用item。因为item不会变。</p>
<p>vue会优先渲染有一一对应关系的,后渲染没有对应关系的。有对应关系的就不重复渲染了,从而提高了性能。</p>
我是怎么学会vue的10:v-if、v-else-if、v-else、v-show
https://segmentfault.com/a/1190000022450204
2020-04-24T01:26:48+08:00
2020-04-24T01:26:48+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>v-if</h2>
<p>功能:通过<code>v-if="ture\false"</code>来决定标签里面的内容是否渲染。</p>
<h2>v-if + v-else</h2>
<p>如果<code>v-if</code>里面的内容为true,渲染<code>v-if</code>的内容,否则渲染<code>v-else</code>的内容。</p>
<pre><code class="html"><div id="app">
<h2 v-if="isShow">
<div>abc</div>
{{message}}
</h2>
<h1 v-else>isShow为false时, 显示我</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isShow: true
}
})
</script></code></pre>
<h2>v-if + v-else-if + v-else</h2>
<p>根据条件判断应该渲染哪个标签的内容。</p>
<pre><code class="html"><div id="app">
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
<h1>{{result}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 99
},
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀'
} else if (this.score >= 80) {
showMessage = '良好'
}
// ...
return showMessage
}
}
})
</script></code></pre>
<h2>v-show</h2>
<p>v-show只是增加了行内样式 display:none,让标签不显示,但这个标签还是被渲染到真实dom里面的。</p>
<p>v-if是根据条件,决定这个元素要不要被渲染到真实dom。</p>
<h4>开发中选择v-if还是v-show?</h4>
<p>如果元素的切换频率很高,使用v-show<br>如果只切换一次,则使用v-if</p>
ES6对象字面量的增强写法
https://segmentfault.com/a/1190000022417980
2020-04-19T01:23:13+08:00
2020-04-19T01:23:13+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<h2>什么是对象字面量</h2>
<p>定义一个对象叫obj</p>
<pre><code class="js">const obj = new Object() //创建一个Object 赋值给obj</code></pre>
<p>但实际开发中我们通常不会这样写,我们一般会写成</p>
<pre><code class="js">const obj = { } //这个大括号就叫对象字面量</code></pre>
<p>上面那个大括号<code>{ }</code>就叫对象字面量。</p>
<h2>对象字面量的写法</h2>
<p>给对象字面量添加一些属性和方法</p>
<pre><code class="js">const obj = {
name: 'why',
age: 18,
run: function () {
console.log('在奔跑');
},
eat: function () {
console.log('在次东西');
}
}</code></pre>
<h2>对象字面量的增强写法</h2>
<h3>属性的增强写法</h3>
<p>定义name,age,height三个属性。</p>
<h4>ES5版本</h4>
<pre><code class="js">const name = 'why';
const age = 18;
const height = 1.88
const obj = {
name: name,
age: age,
height: height
}</code></pre>
<h4>ES6版本</h4>
<pre><code class="js">const name = 'why';
const age = 18;
const height = 1.88
const obj = {
name,
age,
height,
} //给obj添加name、age、height三个属性,值从同名变量找</code></pre>
<h3>函数的增强写法</h3>
<h4>ES5版本</h4>
<pre><code class="js">const obj = {
run: function () {
},
eat: function () {
}
}</code></pre>
<h4>ES6版本</h4>
<pre><code class="js">const obj = {
run() {
},
eat() {
}
}</code></pre>
const的使用和注意点
https://segmentfault.com/a/1190000022413429
2020-04-18T13:53:57+08:00
2020-04-18T13:53:57+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>const用于定义常量,定义完不能修改。</p>
<p>建议在开发中优先使用const,这样可以有效避免变量被其他同事修改的问题。</p>
<p>定义const的时候必须赋值。</p>
<pre><code class="js">const name; //这样写是不允许的,会报错
name = 'abc; </code></pre>
<p>常量的含义是:指向的对象不能修改,但是可以修改对象内部的属性。</p>
<p>指向的对象不能修改:像下面这样</p>
<pre><code class="js">const obj = {
name:'abc',
age:10
}
obj = {} //会报错,修改了常量的指向。</code></pre>
<p><img src="/img/bVbGcTm" alt="image.png" title="image.png"></p>
<p>如图,指向的对象不能修改,是说const obj已经指向右上那个对象了,改成指向下面那个对象,是不能修改的。对象保存在内存中,一个对象对应着一个内存地址(见右侧红字和蓝字)。这个地址保存在const常量中,不能修改指向,也就是说这个内存地址是不允许修改的。</p>
<p>【可以修改对象内部的属性】是说,我们可以<strong>修改内存内部的属性</strong>,而不是<strong>改内存地址</strong>。</p>
<pre><code class="js">const obj = {
name:'abc',
age:10
}
obj.name = 'kobe';//修改对象的name属性
obj.age = '40';//修改对象的age属性</code></pre>
<h2>案例</h2>
<p>之前我们新建Vue实例的时候,通常会这样写:</p>
<pre><code class="js">const app = new Vue({
el:'#app',
data:{
message:'你好啊',
fristName:'kobe
}
})
app.firstName = ''</code></pre>
<p>为什么const还可以修改?因为改的不是内存地址,只是修改了内存内部的属性。</p>
var和let的区别(变量的作用域、闭包)
https://segmentfault.com/a/1190000022399047
2020-04-17T00:45:36+08:00
2020-04-17T00:45:36+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<p><strong>变量的作用域</strong>指的是:变量在什么范围内是可用的。</p>
<p>var 在 if 和 for 中 没有作用域,在 function 中有作用域,即<strong>var 没有块级作用域</strong>。<strong>块</strong>指的是代码块,即 if 和 for 后面的<code>{ }</code>。</p>
<p>let 在 if 和 for 中有作用域,即<strong>let 有块级作用域</strong>。</p>
<h2>没有块级作用域导致的问题</h2>
<p>案例:点击第i个按钮,打印文字:“第i个按钮被点击”。</p>
<pre><code class="html"><button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button></code></pre>
<p>如果我们这样写:</p>
<pre><code class="js">const btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}</code></pre>
<p>看似是 点击按钮1,就console “第1个按钮被点击” ;点击按钮2,就console “第2个按钮被点击”。</p>
<p>但实际上,不管点击哪个按钮,都会提示“第5个按钮被点击”。</p>
<p>为什么会这样?</p>
<p>首先,var没有块级作用域,var虽然定义在for里面,但其实定义的是一个全局变量。</p>
<pre><code class="js">for (var i = 0; i < btns.length; i++) { }</code></pre>
<p>相当于是下面这样:</p>
<pre><code class="js">var i;
for (i = 0; i < btns.length; i++) { }</code></pre>
<p>i都是全局变量。i的作用域是全局。</p>
<p>其次,for循环其实是这样循环的:循环几次,就有几个代码块</p>
<pre><code class="js">{
var i = 0;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{
var i = 1;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{
var i = 2;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{
var i = 3;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{
var i = 4;
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}</code></pre>
<p>这段代码里面,每个代码块有2个i:分别是<code>btns[i]</code>和<code>console.log('第' + i + '个按钮被点击')</code>。</p>
<p><code>btns[i]</code>在循环当时就被执行了,给每个按钮都添加了一个点击事件。但问题的核心在于:<strong>点击事件是在点击的时候,才被执行</strong>。并且点击的时候,i的值已经变成5了。</p>
<p>确切的说,是循环执行完的时候,i就已经变成5了。我们把代码换一种写法可以看得更直观一些:</p>
<pre><code class="js">var i=0; //i的初始值为0
{
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
//首次循环执行完,i++之后,i现在的值是1
{
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
//第二次循环执行完,i++之后,i现在的值是2
{
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
//第三次循环执行完,i++之后,i现在的值是3
{
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
//第四次循环执行完,i++之后,i现在的值是4
{
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
//for循环执行完毕,又执行了一下i++,i现在的值是5
//所以点击事件的时候,代码去找变量i,找到的值就是5</code></pre>
<p>不管点击哪个按钮,都会显示 “第5个按钮被点击”。</p>
<h2>解决方案和原因</h2>
<h3>方案1 使用闭包</h3>
<p>为什么闭包可以解决问题: 因为var在函数里面有作用域。我们可以借助这个特性,让每次循环(每个function)都有自己的<code>i</code>。</p>
<pre><code class="js">var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
(function (num) {
btns[i].addEventListener('click', function () {
console.log('第' + num + '个按钮被点击');
})
})(i)
}</code></pre>
<p>这种写法相当于是:</p>
<pre><code class="js">var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
(function (num) { })(i) //num是形参,把0传给num
(function (num) { })(i) //把i=1传进去
(function (num) { })(i) //把i=2传进去
(function (num) { })(i) //把i=3传进去
(function (num) { })(i) //把i=4传进去
}</code></pre>
<p><code>()()</code>写法解析:<br>第一个<code>()</code>是一个函数整体;第二个<code>()</code>表示立即执行(如果有参数则传递参数进去立即执行),也就是我们需要手动调用一下这个函数,要不函数不会被执行,也就不会给按钮添加上点击事件。</p>
<p>var 在 function 里面有作用域,function 可以在自己函数内部找到变量 i,也就不用到外面找全局的 i 了。</p>
<p>为了避免混淆,这里的形参使用了 num,一般写的时候会把形参和实参都写成i,不要弄混是一个注意点。</p>
<pre><code class="js">var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
(function (num) {
//var num = 0; 相当于是新建了一个变量num
btns[i].addEventListener('click', function () {
console.log('第' + num + '个按钮被点击');
})
})(i)
}</code></pre>
<pre><code class="js">var btns = document.getElementsByTagName('button');
for (var i=0; i<btns.length; i++) {
(function (num) {
//var i = 0; 相当于是新建了一个变量i
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
})(i)
}</code></pre>
<h3>方案2 使用let替代var</h3>
<pre><code class="js">for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}</code></pre>
<p>使用let定义变量i的话,定义在代码块里面的i,只在代码块里面有效(代码块有自己的作用域了,i只属于当前这个大括号<code>{}</code>),在代码块外面是读取不到的。</p>
<p>代码相当于是这样:</p>
<pre><code class="js">{ i = 0
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{ i = 1
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{ i = 2
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{ i = 3
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}
{ i = 4
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}</code></pre>
我是怎么学会vue的09:计算属性(computed属性)
https://segmentfault.com/a/1190000022331123
2020-04-10T17:15:44+08:00
2020-04-10T17:15:44+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>data中的数据,可以通过插值语法直接显示到页面上,但有时需要先处理一下(比如把多个数据合并),再显示到页面。</p>
<p>此时就会用到计算属性(vue实例的computed属性)。</p>
<h2>用法</h2>
<p>HTML:在模板里面指定属性名</p>
<pre><code class="html"><h2>{{fullName}}</h2></code></pre>
<p>JS:在computed属性里面,添加方法。</p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
firstName: 'Lebron',
lastName: 'James'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})</code></pre>
<h2>案例</h2>
<p>显示图书总价</p>
<pre><code class="html"><div id="app">
<h2>总价格: {{totalPrice}}</h2>
<!--totalPrice不需要加小括号-->
</div></code></pre>
<pre><code class="js"><script>
const app = new Vue({
el: '#app',
data: {
books: [
{id: 110, name: 'Unix编程艺术', price: 119},
{id: 111, name: '代码大全', price: 105},
{id: 112, name: '深入理解计算机原理', price: 98},
{id: 113, name: '现代操作系统', price: 87},
]
},
computed: {
totalPrice: function () {
let result = 0
for (let i=0; i < this.books.length; i++) {
result += this.books[i].price
}
return result
//可以直接在for里面return this.books.reduce()
//常用的几个高阶函数有 filter/map/reduce
}
}
//这里的for循环可以使用:
// for (let i in this.books) {
// this.books[i]
// }
//或
// for (let book of this.books) {
//
// }
})
</script></code></pre>
<h2>setter和getter</h2>
<h3>get方法</h3>
<p>为什么计算属性写成函数,使用的时候却当一个普通属性来使用,不用加小括号呢?</p>
<p>因为我们平时使用的,是computed的简写形式,比如上面【用法】这个案例,如果完整写的话,fullName属性的值不是函数,而是对象,对象里面包含set方法和get方法,如下所示:</p>
<pre><code class="js">computed: {
fullName: {
set: function () {
},
get: function () {
return this.firstName + ' ' + this.lastName
}
}
}</code></pre>
<p>使用计算属性,其实是调用get方法,来输出<code>return</code>的内容。</p>
<p><strong>计算属性通常是只读属性</strong>,因为计算属性通常只实现get方法,不需要实现set方法。所以set方法一般会删除掉:</p>
<pre><code class="js">computed: {
fullName: {
get: function () {
return this.firstName + ' ' + this.lastName
}
}
}</code></pre>
<p>既然只实现get方法,每次都写get挺麻烦的,所以我们通常会简写成下面的形式:</p>
<pre><code class="js">computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}</code></pre>
<p>这个function,对应的就是get方法。所以fullName不需要加小括号,因为他是属性,不是方法。之所以看着像方法,只是因为简写了。</p>
<h3>set方法(了解,通常不需要实现set方法)</h3>
<p>要实现set方法,需要给set方法传递一个参数:</p>
<pre><code class="js">set:function(newValue){
console.log(newValue)
}</code></pre>
<p>我们打开浏览器的调试窗口,输入<code>app.fullName='yao kai'</code>,则值<code>yao kai</code>会被传递给newValue,console的结果是<code>yao kai</code>。</p>
<p>拿到newValue之后,我们想把它保存给data,该如何操作呢:</p>
<pre><code class="js">set: function(newValue) {
console.log(newValue);
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}</code></pre>
<h2>计算属性和methods的对比</h2>
<p>计算属性有缓存,methods没有缓存。</p>
<p>如果变量没有变化,那么计算属性就直接把上次的结果返回,而不会重新计算,这样就提升了性能。</p>
我是怎么学会vue的08:v-bind动态绑定style属性
https://segmentfault.com/a/1190000022309447
2020-04-09T01:10:19+08:00
2020-04-09T01:10:19+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>标签的style不写死,而是动态的绑定上去。</p>
<p>在组件化开发的时候常用。</p>
<p>分为两种绑定方式:对象语法和数组语法。对象语法就是把对象绑定给style,数组语法就是把数组绑定给style。</p>
<h2>对象语法</h2>
<pre><code class="html"><h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2></code></pre>
<p>对象是键值对的形式:键是属性名,值是属性的值,不是布尔值。值需要写到引号里面(字符串),不写引号的话则解析成变量名。变量定义在data里面,如下:</p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
finalSize: 100,
finalColor: 'red',
}
})</code></pre>
<hr>
<p>如果键值对比较长,也可以把键值对写到方法里面。</p>
<pre><code class="html"><h2 :style="getStyles()">{{message}}</h2></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
finalSize: 100,
finalColor: 'red',
},
methods: {
getStyles: function () {
return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
}
}
})</code></pre>
<h2>数组语法(不常用)</h2>
<pre><code class="html"><h2 :style="[baseStyle, baseStyle1]">{{message}}</h2></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
baseStyle: {backgroundColor: 'red'},
baseStyle1: {fontSize: '100px'},
}
})</code></pre>
我是怎么学会vue的08:v-bind动态绑定class(对象语法)
https://segmentfault.com/a/1190000022174755
2020-03-27T20:07:11+08:00
2020-03-27T20:07:11+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<p>实际开发中,标签的class通常不会使用一个普通字符串写死,而是动态地给标签绑定class,有2种绑定语法:对象语法和数组语法。</p>
<h2>对象语法(常用)</h2>
<p>绑定方式是使用对象 <code>v-bind:class="{ }"</code>。</p>
<p>对象<code>{ }</code>由键值对构成,键是类名,值是布尔值。</p>
<p>如下所示:</p>
<pre><code class="html"><h2 v-bind:class="{类名1:boolean,类名2:boolean}"></code></pre>
<p>对象语法的核心是:根据布尔值,判断要不要给标签加上某个类。要点在于,Boolean不仅可以通过data定义,还可以通过计算获得(比如使用===来计算true或false)。</p>
<blockquote>使用场景:某标签有时需要加class,有时不需要加class。<br><img src="/img/bVbFcMM" alt="image.png" title="image.png"><br>比如豆瓣电影页面上,【最近热门电影】右边有几个按钮,点谁就把谁的颜色变成黑色,其它按钮的字变成灰色。通过查看控制台可知,点击按钮时给标签添加了一个<code>active</code>类,并去掉别的按钮的<code>active</code>类。</blockquote>
<p>演示1:给标签动态绑定class</p>
<ol>
<li>
<p>dom</p>
<pre><code class="html"><h2 v-bind:class="{active:isActive,line:isLine}">{{message}}</h2></code></pre>
</li>
<li>
<p>数据</p>
<pre><code class="js">data:{
message:"你好",
isActive:true,
isLine:true
}</code></pre>
</li>
</ol>
<p>这样h2的两个类都会显示。</p>
<p>演示2:点一下按钮 h2变红,再点击一下h2变黑</p>
<ol>
<li>
<p>dom</p>
<pre><code class="html"><div id="app">
<h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
<button v-on:click="btnClick">按钮</button>
</div></code></pre>
</li>
<li>
<p>vue实例</p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isActive: true,
isLine: true
},
methods: {
btnClick: function () {
this.isActive = !this.isActive
}
}
})</code></pre>
</li>
</ol>
<h3>练习</h3>
<p>使用v-for加v-bind,实现如下效果:<br><img src="/img/bVbFLLx" alt="image.png" title="image.png"><br>点击哪项,哪项变红。</p>
<p>要点:</p>
<ol>
<li>每个li都有一个index。</li>
<li>判断li和预设值是否相等,来决定动态绑定的class是true还是false。</li>
</ol>
<h3>缩写</h3>
<p>使用 <code>:class</code> 代替 <code>v-bind:class</code></p>
<pre><code class="html"><h2 v-bind:class="{active:isActive,line:isLine}"><!--不缩写-->
<h2 :class="{active:isActive,line:isLine}"><!--缩写--></code></pre>
<h3>如果对象太长了</h3>
<p>可以把对象绑定成一个 methods,或使用 computed 计算属性。</p>
<h4>methods</h4>
<p>在上面的缩写中,我如果觉得<code>{active:isActive,line:isLine}</code>太长了,我可以把这个对象写成一个方法,再把方法放到vue实例的methods属性里面。</p>
<pre><code class="html"><h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
<button v-on:click="btnClick">按钮</button>
<!--注意方法名getClasses()后面要加小括号-->
<!--btnClick不用加小括号的原因是:小括号被省掉了,其实调用函数都得加小括号--></code></pre>
<pre><code class="js"><script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isActive: true,
isLine: true
},
methods: {
btnClick: function () {
this.isActive = !this.isActive //注意要加this
},
getClasses: function () {
return {active: this.isActive, line: this.isLine} //注意要加this
}
}
})
</script></code></pre>
<h4>computed</h4>
<p>后面会讲到。</p>
<h3>建议</h3>
<ol>
<li>固定的class,使用<code>class=""</code>,可能之后会删的,使用<code>v-bind:class="{}"</code>。</li>
<li>
<code>class=""</code>和<code>v-bind:class="{}"</code>可以一起写,不冲突。</li>
</ol>
<h2>数组语法(不常用)</h2>
<p>当类很多的时候,可以把类名都放进一个数组中进行展示。</p>
<pre><code class="html"><h2 class="title" :class="[active, line]">{{message}}</h2></code></pre>
<p>也可以把 <code>[active, line]</code> 放进methods里。</p>
<pre><code class="html"><h2 class="title" :class="getClasses()">{{message}}</h2></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
active: 'aaaaaa',
line: 'bbbbbbb'
},
methods: {
getClasses: function () {
return [this.active, this.line]
}
}
})</code></pre>
我是怎么学会vue的07:v-bind属性绑定(简写用冒号)
https://segmentfault.com/a/1190000022173891
2020-03-27T18:29:55+08:00
2020-03-27T18:29:55+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>我们之前学习了如何绑定标签的内容:使用模板<code>{{ }}</code>(Mustache语法),但这个语法只能用来绑定内容,不能用来绑定标签的属性。</p>
<p>实际开发中,标签的属性不是写死的,需要先从服务器请求,服务器返回json数据,把数据放进vue实例的data,再把data和标签的属性做绑定。</p>
<p>绑定方法:要给哪个属性绑定值,就在该属性前面加上<code>v-bind:</code>即可。</p>
<p>常用到的标签和属性如:a标签的href属性、img标签的src属性等。</p>
<ol>
<li>
<p>html</p>
<pre><code class="html"><img v-bind:src="imgUrl" alt=""/></code></pre>
</li>
<li>
<p>数据</p>
<pre><code class="js">data:{
imgUrl:'abc.com/def.jpg'
}</code></pre>
</li>
</ol>
<blockquote>错误演示:<br><code><img src="{{imgUrl}}"></code>,这样写会把<code>{{imgUrl}}</code>解析成一个字符串,Mustache语法不支持绑定属性,只支持绑定内容,比如这样:<code><h2>{{message}}</h2></code>
</blockquote>
<blockquote>
<p>缩写:使用冒号即可</p>
<pre><code class="html"><img v-bind:src="imgUrl" alt=""/> <!--不缩写-->
<img :src="imgUrl" alt=""/> <!--缩写--></code></pre>
</blockquote>
我是怎么学会vue的06:插值(mustache语法及其他插值指令)
https://segmentfault.com/a/1190000022163557
2020-03-26T23:32:08+08:00
2020-03-26T23:32:08+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>插值的意思就是把数据(值)插进dom里,数据定义在vue实例的data属性中。</p>
<h2>1. 使用Mustache语法(即双大括号<code>{{ }}</code>)</h2>
<p>DOM:</p>
<pre><code class="html"><div id="app">
<h2>{{message}}</h2>
<h2>{{message}}, 李银河!</h2>
<!--mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式-->
<h2>{{firstName + ' ' + lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{counter * 2}}</h2>
</div></code></pre>
<p>vue实例的数据:</p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
message: '你好啊',
firstName: 'kobe',
lastName: 'bryant',
counter: 100
},
})</code></pre>
<h2>2. 其他的插值指令</h2>
<h3>(1)v-once</h3>
<p>只显示第一次解析到的数据,当数据发生改变时,页面不跟着变。</p>
<pre><code class="html"><h2 v-once>{{message}}</h2></code></pre>
<h3>(2)v-html</h3>
<p>如果数据是一个HTML标签的字符串,使用v-html指令,把这个字符串解析成标签 显示到页面上。</p>
<pre><code class="js">data: {
url:"<a href='http://www.baidu.com'>百度一下</a>"
}</code></pre>
<pre><code class="html"><h2>{{url}}</h2> <!--把a标签显示到页面上-->
<h2 v-html="url"></h2> <!--把<a href='http://www.baidu.com'>百度一下</a>这个字符串显示到页面上--></code></pre>
<h3>(3)v-text</h3>
<p>把文本放到DOM上直接展示。</p>
<pre><code class="html"><h2>{{message}}</h2>
<h2 v-text="message"></h2></code></pre>
<p>这两种写法的效果是一样的。区别在于:v-text指令不够灵活,比如下面的演示:</p>
<pre><code class="html"><h2 v-text="message">张伟</h2></code></pre>
<p>message我们定义的是“你好啊”,这样写的话,“你好啊”会把“张伟”覆盖,只显示“你好啊”。</p>
<h3>(4)v-pre</h3>
<p>不做解析,直接显示原本的Mustache语法。</p>
<pre><code class="html"><h2 v-pre>{{message}}</h2></code></pre>
<h3>(5)v-cloak</h3>
<p>给HTML标签添加v-cloak属性后,vue解析标签的时候,如果没数据:保留v-cloak标签;如果有数据:删除v-cloak。</p>
<p>实现效果:配合css使用,可以做到有数据的时候显示标签,没数据的时候隐藏标签。</p>
<p>场景:程序实际的执行中,可能会有代码的卡顿或延迟,导致vue读取不到数据,当读取不到数据时,就会直接显示<code>{{message}}</code>,当解析到数据时,又会把<code>{{message}}</code>修改成数据。这样就会造成页面显示的混乱。我们希望有数据的时候显示标签,没数据的时候隐藏标签。</p>
<p>用法如下:</p>
<pre><code class="html"><h2 v-cloak>{{message}}</h2></code></pre>
<p>数据部分,我们使用一个定时器,模拟程序的卡顿或延迟。</p>
<pre><code class="js">setTimeout(function(){
const app = new Vue({
el:'#app',
data:{
message:'你好'
}
})
},1000)</code></pre>
<p>这样写就实现了我们想要的效果:一开始没数据,<code>v-cloak</code>属性保留,显示<code>{{message}}</code>,后面有数据了,显示<code>你好</code>,并删除了<code>v-cloak</code>属性。</p>
<p>最后,指定css效果:</p>
<pre><code class="css"><style>
[v-cloak] {
display: none;
}
</style></code></pre>
<p>有<code>v-cloak</code>属性的时候,把这个标签隐藏,至此我们想要的效果就做好了。</p>
我的怎么学会vue的05:vue的生命周期
https://segmentfault.com/a/1190000022109335
2020-03-23T09:17:32+08:00
2020-03-23T09:17:32+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p><img src="/img/bVbEVQP" alt="vue的生命周期.jpg" title="vue的生命周期.jpg"></p>
<p>其中created和mounted是比较常用的。</p>
<p>created常用语做网络请求,把请求到的数据存到data里面,data变化dom即自动变化。</p>
js中方法和函数的区别
https://segmentfault.com/a/1190000022105990
2020-03-22T21:14:02+08:00
2020-03-22T21:14:02+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<ol>
<li>单词不一样。<p>方法的英文是method,函数的英文是Function。</p>
</li>
<li>
<p>位置不一样。</p>
<p>写在外面(直接写在<code><script></code>标签里面的)的叫函数,比如这样</p>
<pre><code class="js"><script>
function abc(){
}
</script></code></pre>
<p>写在类里面的叫方法,因为方法和某个实例或对象是绑定的。比如这样:</p>
<pre><code class="js">function Person(){
//写在Person里面的叫方法
}</code></pre>
</li>
</ol>
<p>在JS中,方法和函数都是使用function来定义的,所以有些人不会去特意区分它们,因为js既有函数也有方法。</p>
<p>但是有的语言是有明确区分的,比如Java,它只有方法,没有函数这个概念。</p>
<p>搞清楚js中方法和函数的区别还是很有必要哒,而且并不难,嘿嘿。</p>
我的怎么学会vue的04:vue的options选项
https://segmentfault.com/a/1190000022098747
2020-03-22T00:21:36+08:00
2020-03-22T00:21:36+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<p>我们在创建vue实例的时候,穿进去了一个对象options。<br>这个options中,可以包含哪些选项呢?<br>我们可以看vue的<a href="https://link.segmentfault.com/?enc=a33Ujrb3uofHSVtS4wTfrQ%3D%3D.TkAmdJ04wxrm51YGlIXLOTGZnsEWBLnjappW7C7lpsQZ2NsXSXSvWUlhJxD%2FmaJayCfmQ%2FSoGYjOHgbY20D5iKBARyoXRwWICHOGvNZlCFE%3D" rel="nofollow">官方文档</a>,【选项/xx】都是可以使用的选项。</p>
<p>在之前我们用到了el、data、methods这三个选项,下面总结一下。</p>
<h2>el</h2>
<p>功能:决定了vue管理哪个dom。<br>语法:</p>
<ol>
<li><code>el:'app'</code></li>
<li><code>el:document.querySelector()</code></li>
</ol>
<h2>data</h2>
<p>功能:定义vue实例对象的数据对象<br>语法:<code>data:Object|Function</code></p>
<blockquote>组件的data只能写Function</blockquote>
<h2>methods</h2>
<p>语法:<code>{ [key: string]: Function }</code></p>
<blockquote>
<code>[key: string]</code>的意思是:键是一个字符串</blockquote>
我的怎么学会vue的03:Vue中的MVVM
https://segmentfault.com/a/1190000022093798
2020-03-21T13:51:07+08:00
2020-03-21T13:51:07+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>MVVM框架</h2>
<p>MVVM是 Model View ViewModel 的缩写。这种模式的优点是:改变js中的数据,dom结构就会跟着变化,不用再手动修改dom。</p>
<p>Model是数据,view即dom,ViewModel是让Model和View通信用的。</p>
<p>MVVM的设计思想是:关注Model的变化,让MVVM框架去自动更新DOM,从而把开发者从操作DOM的繁琐步骤中解脱出来!</p>
<h2>VUE中的MVVM</h2>
<p><img src="/img/bVbERKV" alt="VUE中的MVVM" title="VUE中的MVVM"><br>view和model无法直接通信,他们通过viewModel来通信,viewModel做了2件事:</p>
<ol>
<li>数据绑定:把从date里定义的数据,绑定到dom上</li>
<li>监听view的事件,从js里回调对应的函数来执行。</li>
</ol>
<h4>思考:在上一讲的计数器案例中,谁是view,谁是model,谁是viewModel?</h4>
<p>计数器的源码如下:</p>
<pre><code class="html"><div id="app">
<h2>当前计数: {{counter}}</h2>
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0,
message: 'abc'
},
methods: {
add: function () {
console.log('add被执行');
this.counter++
},
sub: function () {
console.log('sub被执行');
this.counter--
}
}
})
</script></code></pre>
<p>在这个计数器示例里面,dom是view,model就是我们在data属性里写的值,是一个对象。为了让这个model显示的更直观更清晰,我们把数据单独拎出来写:</p>
<pre><code class="js"><script>
//把数据写在vue实例外面。
const obj = {
counter: 0,
message: 'abc'
}
const app = new Vue({
el: '#app',
data: obj,
methods: {
add: function () {
console.log('add被执行');
this.counter++ //这里虽然数据写在实例外面,但是this也可以读取到,因为vue在这里做了一个代理,后面会讲到
},
sub: function () {
console.log('sub被执行');
this.counter--
}
}
})
</script></code></pre>
<p>我们创建的那个vue实例<code>new Vue()</code>,就是ViewModel,让Model和View可以进行通信。<br><img src="/img/bVbEUQG" alt="计数器的MVVM.png" title="计数器的MVVM.png"></p>
我的怎么学会vue的02:v-on事件监听(附计数器案例)
https://segmentfault.com/a/1190000022088024
2020-03-20T18:34:18+08:00
2020-03-20T18:34:18+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<h2>给元素添加事件</h2>
<p>给元素添加事件的方法:使用<code>v-on</code>指令。</p>
<p>比如给button元素添加一个点击(click)事件:</p>
<pre><code class="html"><button v-on:click="">我是一个按钮</button>
简写方式:
<button @click="">我是一个按钮</button></code></pre>
<blockquote>
<code>@</code>是<code>v-on:</code>的语法糖(语法糖就是简写的意思)</blockquote>
<p>点击之后要做的事,可以写在<code>click=""</code>这个引号里面(如果只有一句代码的话),也可以定义一个函数,把函数名写在引号里面<code>click="add"</code>。定义函数要在vue实例的<strong>methods属性</strong>里面定义。</p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: obj,
methods: { //在这里定义事件对应的函数
add: function () {
console.log('add被执行');
}
}
})</code></pre>
<h2>案例:计数器</h2>
<p>如下图所示,初始值为0,点击加号,数值+1,反之-1。<br><img src="/img/bVbEQdN" alt="image.png" title="image.png"></p>
<p>第一步:页面布局,并添加点击事件</p>
<pre><code class="html"><div id="app">
<h2>当前计数: {{counter}}</h2>
<button v-on:click="">+</button>
<button v-on:click="">-</button>
</div></code></pre>
<p>第二步:创建vue实例</p>
<pre><code class="js"><script src="../js/vue.js"></script>
<script>
// 初始值
const obj = {
counter: 0
}
const app = new Vue({
el: '#app',
data: obj,
methods: {
//方法都写在这
}
})
</script></code></pre>
<p>第三步:定义点击事件要执行的动作</p>
<p>有两种写法,这里的例子比较简单,可以把代码直接写到标签里面,如下</p>
<pre><code class="html"><div id="app">
<h2>当前计数: {{counter}}</h2>
<button v-on:click="counter++">+</button>
<!--这个counter就是我们定义的那个counter-->
<button v-on:click="counter--;">-</button>
</div></code></pre>
<p>当然大多数情况下,要执行的动作不会这么简单,所以常用下面的写法:</p>
<pre><code class="html"><div id="app">
<h2>当前计数: {{counter}}</h2>
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
<!--下面是语法糖写法-->
<!--<button @click="sub">-</button>-->
</div></code></pre>
<pre><code class="js"><script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add: function () {
console.log('add被执行');
this.counter++ //this指的是当前vue实例的data
},
sub: function () {
console.log('sub被执行');
this.counter--
}
}
})
</script></code></pre>
<blockquote>add和sub,是函数还是方法?<br>是方法。因为add里面的this,和实例app是绑定的,所以add是方法。sub同理。<br>解析:<a href="https://segmentfault.com/a/1190000022105990">js中方法和函数的区别</a>
</blockquote>
<h2>v-on的参数传递</h2>
<h4>1.在事件监听时,且监听的那个事件不需要传递参数,才可以省掉<code>()</code>
</h4>
<pre><code class="html"><button @click="btn1Click()">按钮1</button>
<button @click="btn1Click">按钮1</button></code></pre>
<p>在这个案例中,js不需要传参数进去,此时可以省掉<code>()</code></p>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
},
methods: {
btn1Click() {
console.log("btn1Click");
}
}
})</code></pre>
<h4>2.在定义事件时,函数需要参数,但没有传入,那么函数形参的值为undefined</h4>
<pre><code class="html"><button @click="btn2Click()">按钮2</button></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
},
methods: {
btn2Click(event) {
console.log('--------', event);//-------- undefined
}
}
})</code></pre>
<p>这个案例中,函数需要传入event参数,但是没有传,那么函数形参的值为undefined。</p>
<h4>3.在事件定义时, 写方法时省略了小括号, 但是方法本身需要一个参数, 则Vue会默认将浏览器生成的event事件对象作为参数传入到方法</h4>
<pre><code class="html"><button @click="btn3Click">省略了小括号</button></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
},
methods: {
btn2Click(event) {
console.log('--------', event);//-------- MouseEvent {isTrusted: true, screenX: 133, screenY: 86, clientX: 133, clientY: 15, …}
}
}
})</code></pre>
<h4>4.定义方法时,既需要event对象,又需要其他参数</h4>
<pre><code class="html"><button @click="btn4Click">省略了小括号</button></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
},
methods: {
btn4Click(abc, event) { //需要给传入多个参数
console.log('++++++++', abc, event); //++++++++ MouseEvent {isTrusted: true, screenX: 201, screenY: 92, clientX: 201, clientY: 21, …} undefined
}
}
})</code></pre>
<p>第一种情况,当我还是不写小括号的话,就默认传event,但是只能传给第一个形参,所以这里形参的abc的值就是event对象,形参event的值是undefined。</p>
<p>第二种情况,当我写小括号,并且给方法传值了</p>
<pre><code class="html"><button @click="btn4Click">省略了小括号</button></code></pre>
<pre><code class="js">const app = new Vue({
el: '#app',
data: {
abc:123
},
methods: {
btn4Click(abc, event) { //需要给传入多个参数
console.log('++++++++', abc, event); //++++++++ 123 undefined
}
}
})</code></pre>
<p>此时的event依然是undefined。因为传值的时候直接写event的话,vue会认为event是一个变量名,并从data里面去找(就像找abc那样),找不到就会报undefined。</p>
<p>如果要获取浏览器生成的那个event对象,需要使用<code>$event</code>,而不能直接写<code>event</code>。</p>
<pre><code class="js"> btn4Click(abc, $event)</code></pre>
<h2>v-on的修饰符</h2>
<table>
<thead><tr>
<th>修饰符</th>
<th>作用</th>
<th>演示</th>
</tr></thead>
<tbody>
<tr>
<td><code>.stop</code></td>
<td>阻止事件冒泡</td>
<td><img src="/img/bVbGmtp" alt="image.png" title="image.png"></td>
</tr>
<tr>
<td><code>.prevent</code></td>
<td>阻止默认事件</td>
<td><img src="/img/bVbGmtQ" alt="image.png" title="image.png"></td>
</tr>
<tr>
<td><code>.{keyCode或keyAlias}</code></td>
<td>监听键盘按键</td>
<td><img src="/img/bVbGmtR" alt="image.png" title="image.png"></td>
</tr>
<tr>
<td><code>.native</code></td>
<td>监听组件根元素的原生事件<br>(只用在组件里)</td>
<td> </td>
</tr>
<tr>
<td><code>.once</code></td>
<td>只能触发一次</td>
<td><code><button @click.once="btn2Click">按钮2</button></code></td>
</tr>
</tbody>
</table>
我的怎么学会vue的01:安装与创建实例、v-for遍历数组或对象
https://segmentfault.com/a/1190000022084667
2020-03-20T15:15:13+08:00
2020-03-20T15:15:13+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<h2>安装</h2>
<p>要学习vue,就要先安装它。vue分为开发版本和生产版本,学习和开发的时候使用开发版本;上线的时候使用生产版本。</p>
<p>vue有三种安装方式:</p>
<ol>
<li>cdn引用</li>
<li>本地下载和引入(推荐初学者使用,学习最基本的语法的初期阶段,使用这种方式)</li>
<li>NPM安装</li>
</ol>
<h2>创建实例</h2>
<h3>演示:定义一个div元素的内容</h3>
<h4>如果内容是一个字符串</h4>
<ol>
<li>使用<code>script</code>标签,引入vuejs</li>
<li>
<p>创建一个div元素,用来动态显示vue指定的内容。</p>
<pre><code class="html"><div id="app"></div> </code></pre>
</li>
<li>用new创建vue实例 <code>const app = new Vue({ })</code><p>(实例里面接收一个对象<code>{ }</code>作为参数)</p>
</li>
<li>
<p>给实例传入参数:指定要管理元素(刚才创建的那个div)</p>
<pre><code class="js">const app = new Vue({
el:'#app' //el属性:指定要管理的元素
})</code></pre>
</li>
<li>
<p>给实例传入参数:定义元素的内容</p>
<pre><code class="html"><div id="app">
<h2>{{message}}</h2>
<h2>{{name}}</h2>
</div> </code></pre>
<pre><code class="js">const app = new Vue({
el:'#app',
data:{ //data属性:指定要显示的数据
message:'您好啊',
name:'姚凯'
}
})</code></pre>
</li>
</ol>
<p>同时在标签里面做一个标记</p>
<pre><code class="html"><div id="app">{{messages}}</div></code></pre>
<p>这种写法是<strong>声明式编程</strong>,只需要在div里做一个标记,标记的位置显示什么,由<strong>vue实例</strong>来决定。<strong>vue实例</strong>看到这有一个标记<code>{{messages}}</code>,就会去实例里面找到对应的数据来填充。</p>
<p>这种写法的好处是<strong>数据和界面完全分离</strong>。数据一变,界面自动跟着变。</p>
<p>完整代码:</p>
<pre><code class="html"><div id="app">
<h2>{{message}}</h2>
<h1>{{name}}</h1>
</div>
<div>{{message}}</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
name: '姚凯'
}
})
</script></code></pre>
<h4>如果内容是一个列表(数组)</h4>
<p>让内容显示成一个列表的话,先定义数据(使用数组)</p>
<pre><code class="js">data:{
messages:'你好啊',
movies:['星际穿越','大话西游','少年派','盗梦空间']
}</code></pre>
<p>我们想要使用ul-li这种形式去展示电影名称的话,需要用到vue的<strong>遍历</strong>语法<code>v-for</code>,写法如下:</p>
<pre><code class="html"><div id="app">
<h2>{{message}}</h2>
<ul>
<li v-for='item in movies'>{{item}}</li>
<ul>
</div></code></pre>
<p>v-for也由vue实例来解析。</p>
<p>在v-for里面创建一个变量叫item,把实例中movies属性那个数组里面的每一项都赋值给变量item,所以<code>{{ }}</code>里面写的不是<code>movies</code>,也不是<code>movies[0]</code>,而是<code>item</code>。有4个<code>item</code>,就会创建4个li。</p>
<p>这就是显示列表的方法。</p>
<h2>v-for遍历数组或对象</h2>
<h3>v-for遍历数组</h3>
<h4>不使用下标</h4>
<pre><code class="html"><ul>
<li v-for="item in names">{{item}}</li>
</ul></code></pre>
<h4>使用下标</h4>
<pre><code class="html"><ul>
<li v-for="(item, index) in names">
{{index+1}}.{{item}}
</li>
</ul></code></pre>
<h3>v-for遍历对象</h3>
<h4>获取value</h4>
<pre><code class="html"><ul>
<li v-for="item in info">{{item}}</li>
</ul></code></pre>
<h4>获取key和value 格式: (value, key)</h4>
<pre><code class="html"><ul>
<li v-for="(value, key) in info">{{value}}-{{key}}</li></ul></code></pre>
<h4>获取key和value和index 格式: (value, key, index)</h4>
<pre><code class="html"> <ul>
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
</ul></code></pre>
react 阻止 form 表单提交
https://segmentfault.com/a/1190000021642540
2020-01-23T00:50:24+08:00
2020-01-23T00:50:24+08:00
白话前端
https://segmentfault.com/u/bhqd
0
<p>首先要给form标签绑定onSubmit事件,绑定onClick事件是不行的。</p>
<pre><code class="html"><form onSubmit={this.handleSubmit}></code></pre>
<p>然后,在handleSubmit函数里,阻止默认事件</p>
<pre><code class="js">handleSubmit(event){
event.preventDefault();
}</code></pre>
<p>或者使用</p>
<pre><code class="js">e.stopPropagation()</code></pre>
JavaScript获取可视区、页面内容、浏览器宽高、滚动高度
https://segmentfault.com/a/1190000020950308
2019-11-08T21:48:22+08:00
2019-11-08T21:48:22+08:00
白话前端
https://segmentfault.com/u/bhqd
3
<h2>一、window对象</h2>
<h3>1.1 窗口左上角在屏幕上的 x 坐标 和 y 坐标(只读)</h3>
<pre><code class="js">screenLeft||screenX
screenTop||screenY</code></pre>
<p><img src="/img/bVbz4h4?w=1512&h=548" alt="窗口左上角在屏幕上的 x 坐标 和 y 坐标" title="窗口左上角在屏幕上的 x 坐标 和 y 坐标"></p>
<blockquote>兼容性<br>IE、Safari 和 Opera 支持 ScreenLeft/ScreenTop<br>Firefox、Safari 支持 ScreenX/ScreenY<br>Chrome 都支持</blockquote>
<h3>1.2 文档区宽高(只读)</h3>
<p>文档区宽高(不含工具栏、控制台等,只是网页显示区域的宽高)</p>
<pre><code class="js">innerWidth||document.documentElement.clientWidth||document.body.clientWidth
innerHeight||document.documentElement.clientHeight||document.body.clientHeight</code></pre>
<blockquote>innerWidth是非IE写法,document是IE写法</blockquote>
<p><img src="/img/bVbz4h9?w=666&h=453" alt="文档区宽高" title="文档区宽高"></p>
<h3>1.3 整个浏览器窗口的宽高(只读)outerWidth、outerHeight</h3>
<p>不支持IE,IE没有获取整个浏览器窗口的宽高的方法</p>
<p><img src="/img/bVbz4if?w=666&h=454" alt="整个浏览器窗口的宽高" title="整个浏览器窗口的宽高"></p>
<h3>1.4 pageXOffset、pageYOffset</h3>
<p>设置或读取当前页面相对于窗口显示区左上角的 X/Y 位置</p>
<h2>二、event对象(事件对象)</h2>
<p>在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。所有浏览器都支持event对象,但支持的方式不同。</p>
<p>普通浏览器支持 event<br>ie 678 支持 window.event</p>
<p>所以如果需要兼容旧版浏览器的话,可以采取兼容性的写法</p>
<pre><code class="js">var event = event || window.event;</code></pre>
<h3>2.1 event.screenX event.screenY</h3>
<p>返回事件发生时,鼠标指针相对于显示器屏幕的左上角水平/垂直坐标</p>
<pre><code class="js"><body>
<div id="box"></div>
<script>
var box = document.getElementById('box');
box.addEventListener('click',function(event){
var screenX = event.screenX;
var screenY = event.screenY;
alert('指针相对于屏幕显示器左上角的X坐标是:'+screenX+', 指针相对于屏幕显示器左上角的Y坐标是:'+screenY);
})
</script></code></pre>
<h3>2.2 event.clientX event.clientY</h3>
<p>返回事件发生时,鼠标指针相对于浏览器窗口可视区域的水平/垂直窗口坐标。</p>
<p>可视区域不包括工具栏和滚动条,IE事件和w3c标准事件都定义了这2个属性</p>
<pre><code class="js"><div id="box"></div>
<script>
var box = document.getElementById('box');
box.addEventListener('click',function(event){
var clientX = event.clientX;
var clientY = event.clientY;
alert('指针相对于文档可视区的X坐标是:'+clientX+', 指针相对于文档可视区的Y坐标是:'+clientY);
})
</script></code></pre>
<h3>2.3 event.offsetX event.offsetY(仅IE)</h3>
<p>返回鼠标指针的位置,相对于事件源元素的水平(垂直坐标)</p>
<pre><code class="JS"><body>
<div id="box"></div>
<script>
var box = document.getElementById('box');
box.addEventListener('click',function(event){
var offsetX = event.offsetX;
var offsetY = event.offsetY;
alert('指针相对于box的X坐标是:'+offsetX+', 指针相对于box的Y坐标是:'+offsetY);
})
</script></code></pre>
<h3>2.4 event.pageX event.pageY(不支持IE)</h3>
<p>返回鼠标指针的位置,类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。</p>
<h2>三、document对象</h2>
<h3>3.1 文档区宽高:clientWidth/clientHeight</h3>
<p>示例图见1.2小节</p>
<pre><code class="js">document.documentElement.clientWidth || document.body.clientWidth
document.documentElement.clientHeight || document.body.clientHeight</code></pre>
<h3>3.2 html(body)的总宽高:offsetWidth/offsetHeight</h3>
<pre><code class="js">document.documentElement.offsetWidth || document.body.offsetWidth
document.documentElement.offsetHeight || document.body.offsetHeight</code></pre>
<p>获取某个元素的总宽高,也可以使用offsetWidth/offsetHeight方法。</p>
<h3>3.3 滚动高度(滚动条滚过的长度)</h3>
<pre><code class="js">document.documentElement.scrollTop || document.body.scrollTop</code></pre>
<h2>四、DOM对象(元素)的offset家族</h2>
<h3>4.1 计算元素宽高:offsetWidth 、 offsetHeight</h3>
<p>offsetWidth 计算方法如下:</p>
<p><strong>offsetWidth = width + border + padding</strong></p>
<h3>4.2 获取位置 offsetLeft 和 offsetTop</h3>
<p>返回距离上级盒子(最近的带有定位)左边的位置。</p>
<p>如果父级都没有定位则以body 为准。</p>
<p>offsetLeft 从父级的padding 开始算 父亲的border 不算,即:就是子盒子到定位的父盒子边框到边框的距离。</p>
<h3>4.3 获取带有定位的父级元素 offsetParent</h3>
<p>offsetParent返回该对象的父级(带有定位)</p>
<h3>4.4 offsetParent 和 parentNode的区别:</h3>
<p>如果当前元素的父级元素没有进行CSS定位(position为absolute或relative),offsetParent为body。</p>
<p>如果当前元素的父级元素中有CSS定位(position为absolute或relative),offsetParent取最近的那个父级元素。</p>
<h3>4.5 offsetTop 和 style.top 的区别</h3>
<ol>
<li>最大区别在于 offsetLeft 可以返回没有定位盒子的距离左侧的位置。 而 style.top 不可以 只有定位的盒子 才有 left top right</li>
<li>offsetTop 返回的是数字,而 style.top 返回的是字符串,除了数字外还带有单位:px。</li>
<li>offsetTop 只读,而 style.top 可读写。</li>
<li>如果没有给 HTML 元素指定过 top 样式,则 style.top 返回的是空字符串。</li>
<li>最重要的区别 style.left 只能得到 行内样式 offsetLeft 随便</li>
</ol>
<h3>4.6 附:获取行内样式的宽高 ele.style.width/height</h3>
<p><code>div.style.width</code> 只能获取到行内样式,无法获取css样式表里面的样式。</p>
<h2>五、Window 对象方法</h2>
<h3>5.1 moveBy( )</h3>
<p>语法: window.moveBy(x,y)</p>
<p>相对窗口当前的坐标,把它移动到指定的像素</p>
<p>x:要把窗口右移的像素</p>
<p>y: 要把窗口下移的像素</p>
<h3>5.2 moveTo( )</h3>
<p>语法: window.moveTo(x,y)</p>
<p>把窗口的左上角移动到一个指定的坐标</p>
<p>x:窗口新位置的 x 坐标</p>
<p>y: 窗口新位置的 y 坐标</p>
<p>提示:出于安全方面的原因,浏览器限制此方法使其不能把窗口移出屏幕。</p>
<h3>5.3 resizeBy( )</h3>
<p>语法:resizeBy(width, height)</p>
<p>根据指定的像素来调整窗口的大小</p>
<p>width:必需,要使窗口宽度增加的像素数。可以是正、负数值</p>
<p>height:可选,要使窗口高度增加的像素数。可以是正、负数值</p>
<h3>5.4 resizeTo( )</h3>
<p>语法:resizeTo(width, height)</p>
<p>把窗口的大小调整为指定的宽度和高度</p>
<p>width:必需,想要调整到的窗口的宽度。以像素计</p>
<p>height:可选,想要调整到的窗口的高度。以像素计</p>
<h3>5.5 scrollBy( )</h3>
<p>语法:scrollBy(xnum, ynum)</p>
<p>把内容滚动指定的像素数</p>
<p>xnum:必需,把文档向右滚动的像素数</p>
<p>ynum:必需,把文档向下滚动的像素数</p>
<h3>5.6 scrollTo( )</h3>
<p>语法:scrollTo(xpos, ypos)</p>
<p>把内容滚动到指定的坐标</p>
<p>xpos:必需,要在窗口文档显示区左上角显示的文档的 x 坐标</p>
<p>ypos:必需,要在窗口文档显示区左上角显示的文档的 y 坐标</p>
获取鼠标位置(区分event对象中的 clientX、offsetX、screenX、pageX )
https://segmentfault.com/a/1190000020949241
2019-11-08T18:25:03+08:00
2019-11-08T18:25:03+08:00
白话前端
https://segmentfault.com/u/bhqd
10
<p>作用:都是用来获取鼠标的位置</p>
<h2>clientX clientY</h2>
<pre><code class="js">event.clientX
event.clientY</code></pre>
<p><img src="/img/bVbz30H?w=814&h=434" alt="image.png" title="image.png"><br>client直译就是客户端,客户端的窗口就是指游览器的显示页面内容的窗口大小(不包含工具栏、导航栏等等)。</p>
<p>event.clientX、event.clientY就是用来获取鼠标距游览器显示窗口的长度。</p>
<blockquote>兼容性:IE和主流游览器都支持。</blockquote>
<h2>offsetX offsetY</h2>
<pre><code class="js">event.offsetX
event.offsetY</code></pre>
<p>offset意为偏移量,是被点击的元素距左上角为参考原点的长度,而IE、FF和Chrome的参考点有所差异。</p>
<p>Chrome下,offsetX offsetY是包含边框的,如图所示。<br><img src="/img/bVbz30I?w=626&h=471" alt="image.png" title="image.png"><br>而IE、FF是不包含边框的,如果鼠标进入到border区域,为返回负值,如图所示。<br><img src="/img/bVbz30M?w=468&h=417" alt="image.png" title="image.png"></p>
<blockquote>兼容性:IE9+,chrome,FF都支持此属性。</blockquote>
<h2>screenX screenY</h2>
<pre><code class="js">event.screenX
event.screenY</code></pre>
<p>screen顾名思义是屏幕,是用来获取鼠标点击位置到屏幕显示器的距离,距离的最大值需根据屏幕分辨率的尺寸来计算。</p>
<blockquote>兼容性:所有游览器都支持此属性。</blockquote>
<h2>pageX pageY</h2>
<pre><code class="js">event.pageX
event.pageY</code></pre>
<p>page为页面的意思,页面的高度一般情况client游览器显示区域装不下,所以会出现垂直滚动条。</p>
<p>鼠标距离页面初始page原点的长度。<br><img src="/img/bVbz30P?w=652&h=737" alt="image.png" title="image.png"><br>在IE中没有pageX、pageY,取而代之的是event.x、event.y 。x和y在webkit内核下也实现了,所以火狐不支持x,y。</p>
<blockquote>兼容性:IE不支持,其他高级游览器支持。</blockquote>
jQuery 中的显式遍历(explicitly iterate)与隐式遍历(implicit iteration)
https://segmentfault.com/a/1190000017777837
2019-01-05T19:50:18+08:00
2019-01-05T19:50:18+08:00
白话前端
https://segmentfault.com/u/bhqd
1
<h2>jQuery 中的显式遍历(explicitly iterate)与隐式遍历(implicit iteration)</h2>
<p>在 jQuery 中,我们把 <code>$()</code> 叫做<strong>核心函数</strong>。</p>
<p>如果通过核心函数找到的元素不止一个,那么在添加事件(或添加类)的时候,jQuery 会遍历所有找到的元素,给所有找到的元素添加事件(或类)。这就叫做“隐式遍历”。</p>
<p>举一个用隐式遍历添加事件的例子:</p>
<p>html:</p>
<pre><code class="html"><ul>
<li>我是第1个li</li>
<li>我是第2个li</li>
<li>我是第3个li</li>
</ul></code></pre>
<p>js:</p>
<pre><code class="js">$("ul>li").click(function () {
console.log($(this).html());
});</code></pre>
<p>这样就给所有的 li 都添加了一个 click 事件。</p>
<p>再举一个用隐式遍历添加类的例子:</p>
<p>html:</p>
<pre><code class="html"><ul>
<li>foo</li>
<li>bar</li>
</ul></code></pre>
<p>js:</p>
<pre><code class="js">$( "li" ).addClass( "bar" );</code></pre>
<p>还是这个功能,我使用一个显式遍历的方法 <code>.each()</code>方法 来写一下。</p>
<pre><code class="js">$( "li" ).each(function() {
$( this ).addClass( "foo" );
});</code></pre>
<p>这个例子应该就比较明确了,都是遍历选出来的一堆元素,在显式遍历中,每遍历到一个元素,我都得指定一下给该元素添加哪个类,显然这样写比较啰嗦;而隐式遍历就方便很多,要批量把核心函数找出来的所有元素都添加同一个事件(或一个类),推荐使用隐式遍历的写法。</p>
<p>下面我再举一个用显式遍历来写的例子:<br>(HTML 部分的代码和上面一样)</p>
<pre><code class="js">$( "li" ).each(function( index ) {
console.log( index + ": " + $( this ).text() );
});</code></pre>
<p><code>.each()</code>方法就是典型的显式遍历:首先我用选择器选中了所有的 li 元素,形成了一个 jQuery 对象,这个对象是长这样的(是一个伪数组):</p>
<pre><code class="js">[ { 0:li } , { 1:li } ]</code></pre>
<p><img src="/img/bVbmKZo?w=600&h=156" alt="clipboard.png" title="clipboard.png"></p>
<p>每个元素还有自己的一大堆属性……<br><img src="/img/bVbmKZr?w=892&h=716" alt="clipboard.png" title="clipboard.png"></p>
<p>(太多了,只截取一部分)</p>
<p>跑题了,回到 .each() 方法。当遍历到第一个 li 元素的时候,就打印第一条信息;遍历到第二个 li 的时候,再打印第二条信息。<br><img src="/img/bVbmKZs?w=368&h=76" alt="clipboard.png" title="clipboard.png"></p>
<p>再回顾一下刚才那一段代码:</p>
<pre><code class="js">$( "li" ).each(function( index ) {
console.log( index + ": " + $( this ).text() );
});</code></pre>
<p>index 是一个形参,表示当前轮到的那个元素的下标。既然是形参,不用 index,用 i 也行。</p>
<p>那么问题来了:为什么这个 function 里写一个参数,这个参数就代表下标了呢?<br>答案是:<br>.each() 方法的 function,它有2个参数。.each 方法的写法是这样的:</p>
<pre><code class="js">$(selector).each(function( index , element ){
//do something
})</code></pre>
<p>function 的第一个参数是整数的下标,第二个参数用于:返回当前读取到的那个元素,比如刚才的代码,element 就是返回每个 li。所以还是刚才的代码,我们也可以这样写:</p>
<pre><code class="js">$( "li" ).each(function( i,element ) {
console.log($(element));
console.log( i + ": " + $(element).text() );
});</code></pre>
<p>有时候我们不写 element,只是因为使用 this 关键词可以替代 element。this 等于 element,同样,<code>$(this)</code>和<code>$(element)</code>是一样的。使用<code>$()</code>选择器选中 this 或者 element 元素之后,我们就可以使用 jQuery 提供的 api,很方便的操作他们,比如,使用 text()方法读取元素的文本内容,或者使用 css()方法修改样式,等等。</p>
<blockquote>总结一下,本文一开始说明了什么是显式遍历,什么是隐式遍历以及二者的区别,然后提到了一个显式遍历方法 <code>.each()</code> 的具体用法。</blockquote>
伪元素 ::after 和 ::before 应该这么用(一)
https://segmentfault.com/a/1190000015468617
2018-07-03T21:34:23+08:00
2018-07-03T21:34:23+08:00
白话前端
https://segmentfault.com/u/bhqd
20
<h2>1 什么是伪元素?</h2>
<p>CSS 在渲染文档的时候,伪元素可以通过 css 给 HTML 添加一个元素(叫标签也行),这个元素在文档树中是找不到的。伪元素被当做 CSS 的样式来进行展现,用法和普通的元素用法是一样的。</p>
<h3>1.1 常用的伪元素和伪类</h3>
<p>伪元素和伪类的写法有点像,伪元素使用2个冒号,常见的有:<code>::before</code>,<code>::after</code>,<code>::first-line</code>,<code>::first-letter</code>,<code>::selection</code>、<code>::placeholder</code>等;伪类使用1个冒号,常见的有:<code>:hover</code>,<code>:link</code>,<code>:active</code>,<code>:target</code>,<code>:not()</code>,<code>:focus</code>等。</p>
<h3>1.2 伪元素和伪类的区别</h3>
<p>伪元素添加了一个页面中没有的元素(只是从视觉效果上添加了,不是在文档树中添加),伪类是给页面中已经存在的元素添加一个类。</p>
<h3>1.3 CSS3中的伪元素</h3>
<p>在实际的开发工作中,有人会把伪元素用1个冒号来写,这实际是 CSS2 与 CSS3新旧标准的规定不同而导致的。<br>CSS2 中的伪元素使用1个冒号,在 CSS3 中,为了区分伪类和伪元素,规定伪元素使用2个冒号。所以,对于 CSS2 标准的老伪元素,比如<code>:first-line</code>,<code>:first-letter</code>,<code>:before</code>,<code>:after</code>,写一个冒号浏览器也能识别,但对于 CSS3 标准的新伪元素,比如<code>::selection</code>,就必须写2个冒号了。<br>如果不需要考虑IE8以下的浏览器,建议都使用2个冒号的新标准写法来写。否则还是使用1个冒号吧!</p>
<h2>2 伪元素 ::before 和 ::after 的用法</h2>
<ol>
<li>标准写法是双冒号(但考虑兼容性也有人写单冒号)</li>
<li>
<code>::before</code>和<code>::after</code>在被选中元素里面、元素现有内容之前(后)插入内容,需要使用<code>content</code>属性指定要插入的内容。<code>content</code>必须有值(空值也行)。</li>
<li>
<code>content</code>插入的内容默认是 inline 元素,可以通过<code>display:block</code>改变。</li>
</ol>
<h2>3 伪元素的特点</h2>
<h3>3.1 优点</h3>
<ol>
<li>不占用 DOM 节点,减少 DOM 节点数。</li>
<li>让 CSS 帮助解决了一部分 JavaScript 问题,简化了开发。</li>
<li>不仅块级元素可以设置伪元素,大部分行级元素也可以。</li>
<li>避免增加毫无意义的页面元素。</li>
</ol>
<h3>3.2 缺点</h3>
<ol>
<li>不利于调试。</li>
<li>伪元素不真正在文档内容中体现,只在视觉效果上体现,所以不能给伪元素添加具有实际意义的内容,这部分内容不会搜索引擎抓取。</li>
</ol>
<h2>4 content属性</h2>
<p>content 的值可以是空值、字符串、attr()、url()、uri()、counter()等,下面分别演示一下。</p>
<h3>4.1 空值</h3>
<p>可以用来清除浮动。代码如下:</p>
<pre><code class="css"> <style>
.clearFix::after{
clear: both;
content: "";
display: block;
height: 0;
overflow: hidden;
}
</style></code></pre>
<p>包含 cleafFix 这个类的元素,其内部的浮动可以被清除。</p>
<h3>4.2 string(字符串)</h3>
<p>在电话号码前面添加一个电话的icon图标。代码如下:</p>
<p>HTML部分</p>
<pre><code class="html"><p class="phoneNumber">13900001390</p></code></pre>
<hr>
<p>CSS部分</p>
<pre><code> .phoneNumber::before{
content:'\260E';
font-size: 16px;
}</code></pre>
<p>效果展示:<br><img src="/img/bVbc4aF?w=250&h=66" alt="图片描述" title="图片描述"></p>
<blockquote>小电话的图标是一个<strong>特殊字符</strong>,每个特殊字符都有特定代码,特定代码用在HTML中以 开头,用在 CSS 中以 \ 开头,用在 JS 中以 \u 开头。<br>比如这个电话图标,在 HTML 中用 <code>&#9742</code> 表示,在 css 中以 <code>\260E</code> 表示,在 JavaScript 中,以 <code>\u260E</code> 表示。</blockquote>
<h3>4.3 attr( ) 以及 url( )</h3>
<p>attr():可以调用当前元素的某个属性。<br> url():可以引用媒体文件(比如图片)。</p>
<hr>
<p>演示代码:</p>
<p>HTML部分:</p>
<pre><code class="html"><a href="https://www.google.com">GOOGLE!</a></code></pre>
<hr>
<p>CSS 部分:</p>
<pre><code class="css"> a::before{
content: url("https://www.google.com/logos/doodles/2018/world-cup-2018-day-19-6211393811382272.3-law.gif");
}
a::after{
content: "("attr(href)")";
}
a{
text-decoration: none;
}</code></pre>
<p>效果预览:<br><img src="/img/bVbc4ds?w=1574&h=458" alt="图片描述" title="图片描述"></p>
<blockquote>例子中,<code>content</code> 中的 <code>url</code> 是引用媒体文件,所有不用加引号。<br><code>attr</code> 是调用元素 <code>href</code> 属性当做字符串显示出来,所有要加引号。至于括号里面,如果加引号,就会显示出来 <code>href</code> 的值,如果不加引号就打印字符串 <code>(attr(href))</code>,我还不太清除加载括号里面的引号是什么用法。</blockquote>
<h3>4.4 counter 计数器</h3>
<p>counter 调用计数器,可以不使用列表元素实现序号功能。<br>counter 要配合 counter-increment 和 counter-reset属性使用。</p>
<blockquote>counter-reset 给同级元素增加计数器,比如一个页面有多个H1元素,那就给body加计数器。一个H1元素里面有多个H2元素,那就给H1元素加计数器。<p>counter-increment 增加计数器数值。</p>
</blockquote>
<p>代码演示:</p>
<p>HTML部分:</p>
<pre><code><body>
<h1>哺乳动物</h1>
<h2>狗</h2>
<h2>猴子</h2>
<h2>猩猩</h2>
<h1>冷血动物</h1>
<h2>鱼</h2>
<h2>蛇</h2>
</body></code></pre>
<hr>
<p>CSS部分:</p>
<pre><code> body{
counter-reset: section;
}
h1{
counter-reset:subsection;
}
h1::before{
counter-increment: section;
content:counter(section)'、';
}
h2:before{
counter-increment: subsection;
content: counter(section)'.'counter(subsection)'、';
}</code></pre>
<p>效果演示:<br><img src="/img/bVbc4fR?w=408&h=756" alt="图片描述" title="图片描述"></p>
<h3>4.5 content的值什么时候加引号,什么时候不加</h3>
<p>通过以上实例可以看出:</p>
<ol>
<li>动态的(会变的值)不加引号。</li>
<li>媒体不加引号。</li>
<li>固定的值、字符串需要加引号。</li>
</ol>