vue学习三

定义Vue组件

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

全局组件定义的三种方式

  1. 使用 Vue.extend 配合 Vue.component 方法:
1
2
3
4
var login = Vue.extend({
template: '<h1>登录</h1>'
});
Vue.component('login', login);
  1. 直接使用 Vue.component 方法:
1
2
3
Vue.component('register', {
template: '<h1>注册</h1>'
});
  1. 将模板字符串,定义到script标签种:
1
2
3
<script id="tmpl" type="x-template">
<div><a href="#">登录</a> | <a href="#">注册</a></div>
</script>

同时,需要使用 Vue.component 来定义组件:

1
2
3
Vue.component('account', {
template: '#tmpl'
});

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

组件中展示数据和响应事件

  1. 在组件中,data需要被定义为一个方法,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component('account', {
template: '#tmpl',
data() {
return {
msg: '大家好!'
}
},
methods:{
login(){
alert('点击了登录按钮');
}
}
});
  1. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问;

【重点】为什么组件中的data属性必须定义为一个方法并返回一个对象

  1. 通过计数器案例演示

使用components属性定义局部子组件

  1. 组件实例定义方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: { // 定义子组件
account: { // account 组件
template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>', // 在这里使用定义的子组件
components: { // 定义子组件的子组件
login: { // login 组件
template: "<h3>这是登录组件</h3>"
}
}
}
}
});
</script>
  1. 引用组件:
1
2
3
<div id="app">
<account></account>
</div>

使用flag标识符结合v-ifv-else切换组件

  1. 页面结构:
1
2
3
4
5
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<my-com1 v-if="flag"></my-com1>
<my-com2 v-else="flag"></my-com2>
</div>
  1. Vue实例定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
Vue.component('myCom1', {
template: '<h3>奔波霸</h3>'
})

Vue.component('myCom2', {
template: '<h3>霸波奔</h3>'
})

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true
},
methods: {}
});
</script>

使用:is属性来切换不同的子组件,并添加切换动画

  1. 组件实例定义方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 登录组件
const login = Vue.extend({
template: `<div>
<h3>登录组件</h3>
</div>`
});
Vue.component('login', login);

// 注册组件
const register = Vue.extend({
template: `<div>
<h3>注册组件</h3>
</div>`
});
Vue.component('register', register);

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: { comName: 'login' },
methods: {}
});
  1. 使用component标签,来引用组件,并通过:is属性来指定要加载的组件:
1
2
3
4
5
6
7
8
<div id="app">
<a href="#" @click.prevent="comName='login'">登录</a>
<a href="#" @click.prevent="comName='register'">注册</a>
<hr>
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</div>
  1. 添加切换样式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(30px);
}

.v-enter-active,
.v-leave-active {
position: absolute;
transition: all 0.3s ease;
}

h3{
margin: 0;
}
</style>

父组件向子组件传值

  1. 组件实例定义方式,注意:一定要使用props属性来定义父组件传递过来的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '这是父组件中的消息'
},
components: {
son: {
template: '<h1>这是子组件 --- {{finfo}}</h1>',
props: ['finfo']
}
}
});
</script>
  1. 使用v-bind或简化指令,将数据传递到子组件中:
1
2
3
<div id="app">
<son :finfo="msg"></son>
</div>

子组件向父组件传值

  1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  2. 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
1
<son @func="getMsg"></son>
  1. 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="app">
<!-- 引用父组件 -->
<son @func="getMsg"></son>

<!-- 组件模板定义 -->
<script type="x-template" id="son">
<div>
<input type="button" value="向父组件传值" @click="sendMsg" />
</div>
</script>
</div>

<script>
// 子组件的定义方式
Vue.component('son', {
template: '#son', // 组件模板Id
methods: {
sendMsg() { // 按钮的点击事件
this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去
}
}
});

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
alert(val);
}
}
});
</script>

评论列表案例

目标:主要练习父子组件之间传值

使用 this.$refs 来获取元素和组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<div id="app">
<div>
<input type="button" value="获取元素内容" @click="getElement" />
<!-- 使用 ref 获取元素 -->
<h1 ref="myh1">这是一个大大的H1</h1>

<hr>
<!-- 使用 ref 获取子组件 -->
<my-com ref="mycom"></my-com>
</div>
</div>

<script>
Vue.component('my-com', {
template: '<h5>这是一个子组件</h5>',
data() {
return {
name: '子组件'
}
}
});

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
// 通过 this.$refs 来获取元素
console.log(this.$refs.myh1.innerText);
// 通过 this.$refs 来获取组件
console.log(this.$refs.mycom.name);
}
}
});
</script>

什么是路由

  1. 对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;

  2. 对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;

  3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

在 vue 中使用 vue-router

  1. 导入 vue-router 组件类库:
1
2
<!-- 1. 导入 vue-router 组件类库 -->
<script src="./lib/vue-router-2.7.0.js"></script>
  1. 使用 router-link 组件来导航
1
2
3
<!-- 2. 使用 router-link 组件来导航 -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
  1. 使用 router-view 组件来显示匹配到的组件
1
2
<!-- 3. 使用 router-view 组件来显示匹配到的组件 -->
<router-view></router-view>
  1. 创建使用Vue.extend创建组件
1
2
3
4
5
6
7
8
9
// 4.1 使用 Vue.extend 来创建登录组件
var login = Vue.extend({
template: '<h1>登录组件</h1>'
});

// 4.2 使用 Vue.extend 来创建注册组件
var register = Vue.extend({
template: '<h1>注册组件</h1>'
});
  1. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则
1
2
3
4
5
6
7
// 5. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则
var router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ path: '/register', component: register }
]
});
  1. 使用 router 属性来使用路由规则
1
2
3
4
5
// 6. 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
router: router // 使用 router 属性来使用路由规则
});

设置路由高亮

设置路由切换动效

在路由规则中定义参数

  1. 在规则中定义参数:
1
{ path: '/register/:id', component: register }
  1. 通过 this.$route.params来获取路由中的参数:
1
2
3
var register = Vue.extend({
template: '<h1>注册组件 --- {{this.$route.params.id}}</h1>'
});

使用 children 属性实现路由嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<div id="app">
<router-link to="/account">Account</router-link>

<router-view></router-view>
</div>

<script>
// 父路由中的组件
const account = Vue.extend({
template: `<div>
这是account组件
<router-link to="/account/login">login</router-link> |
<router-link to="/account/register">register</router-link>
<router-view></router-view>
</div>`
});

// 子路由中的 login 组件
const login = Vue.extend({
template: '<div>登录组件</div>'
});

// 子路由中的 register 组件
const register = Vue.extend({
template: '<div>注册组件</div>'
});

// 路由实例
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/account/login' }, // 使用 redirect 实现路由重定向
{
path: '/account',
component: account,
children: [ // 通过 children 数组属性,来实现路由的嵌套
{ path: 'login', component: login }, // 注意,子路由的开头位置,不要加 / 路径符
{ path: 'register', component: register }
]
}
]
});

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
account
},
router: router
});
</script>

命名视图实现经典布局

  1. 标签代码结构:
1
2
3
4
5
6
7
<div id="app">
<router-view></router-view>
<div class="content">
<router-view name="a"></router-view>
<router-view name="b"></router-view>
</div>
</div>
  1. JS代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<script>
var header = Vue.component('header', {
template: '<div class="header">header</div>'
});

var sidebar = Vue.component('sidebar', {
template: '<div class="sidebar">sidebar</div>'
});

var mainbox = Vue.component('mainbox', {
template: '<div class="mainbox">mainbox</div>'
});

// 创建路由对象
var router = new VueRouter({
routes: [
{
path: '/', components: {
default: header,
a: sidebar,
b: mainbox
}
}
]
});

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
</script>
  1. CSS 样式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.header {
border: 1px solid red;
}

.content{
display: flex;
}
.sidebar {
flex: 2;
border: 1px solid green;
height: 500px;
}
.mainbox{
flex: 8;
border: 1px solid blue;
height: 500px;
}
</style>

watch属性的使用

考虑一个问题:想要实现 两个文本框的内容改变,则全名的文本框中的值也跟着改变;(用以前的知识如何实现???)

  1. 监听data中属性的改变:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
<input type="text" v-model="firstName"> +
<input type="text" v-model="lastName"> =
<span>{{fullName}}</span>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen',
fullName: 'jack - chen'
},
methods: {},
watch: {
'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
this.fullName = newVal + ' - ' + this.lastName;
},
'lastName': function (newVal, oldVal) {
this.fullName = this.firstName + ' - ' + newVal;
}
}
});
</script>
  1. 监听路由对象的改变:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<div id="app">
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>

<router-view></router-view>
</div>

<script>
var login = Vue.extend({
template: '<h1>登录组件</h1>'
});

var register = Vue.extend({
template: '<h1>注册组件</h1>'
});

var router = new VueRouter({
routes: [
{ path: "/login", component: login },
{ path: "/register", component: register }
]
});

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: router,
watch: {
'$route': function (newVal, oldVal) {
if (newVal.path === '/login') {
console.log('这是登录组件');
}
}
}
});
</script>

computed计算属性的使用

  1. 默认只有getter的计算属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<input type="text" v-model="firstName"> +
<input type="text" v-model="lastName"> =
<span>{{fullName}}</span>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen'
},
methods: {},
computed: { // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发 本计算属性 的重新计算,从而更新 fullName 的值
fullName() {
return this.firstName + ' - ' + this.lastName;
}
}
});
</script>
  1. 定义有gettersetter的计算属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<div id="app">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<!-- 点击按钮重新为 计算属性 fullName 赋值 -->
<input type="button" value="修改fullName" @click="changeName">

<span>{{fullName}}</span>
</div>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen'
},
methods: {
changeName() {
this.fullName = 'TOM - chen2';
}
},
computed: {
fullName: {
get: function () {
return this.firstName + ' - ' + this.lastName;
},
set: function (newVal) {
var parts = newVal.split(' - ');
this.firstName = parts[0];
this.lastName = parts[1];
}
}
}
});
</script>

watchcomputedmethods之间的对比

  1. computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  2. methods方法表示一个具体的操作,主要书写业务逻辑;
  3. watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computedmethods的结合体;

nrm的安装使用

作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址;
什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样;

  1. 运行npm i nrm -g全局安装nrm包;
  2. 使用nrm ls查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;
  3. 使用nrm use npmnrm use taobao切换不同的镜像源地址;

有时候使用npm i node-sass -D装不上,这时候,就必须使用 cnpm i node-sass -D

在普通页面中使用render函数渲染组件

在webpack中配置.vue组件页面的解析

  1. 运行cnpm i vue -S将vue安装为运行依赖;

  2. 运行cnpm i vue-loader vue-template-compiler -D将解析转换vue的包安装为开发依赖;

  3. 运行cnpm i style-loader css-loader -D将解析转换CSS的包安装为开发依赖,因为.vue文件中会写CSS样式;

  4. webpack.config.js中,添加如下module规则:

1
2
3
4
5
6
7
8
9
10
11
module: {

rules: [

{ test: /\.css$/, use: ['style-loader', 'css-loader'] },

{ test: /\.vue$/, use: 'vue-loader' }

]

}
  1. 创建App.js组件页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>

<!-- 注意:在 .vue 的组件中,template 中必须有且只有唯一的根元素进行包裹,一般都用 div 当作唯一的根元素 -->

<div>

<h1>这是APP组件 - {{msg}}</h1>

<h3>我是h3</h3>

</div>

</template>



<script>

// 注意:在 .vue 的组件中,通过 script 标签来定义组件的行为,需要使用 ES6 中提供的 export default 方式,导出一个vue实例对象

export default {

data() {

return {

msg: 'OK'

}

}

}

</script>



<style scoped>

h1 {

color: red;

}

</style>
  1. 创建main.js入口文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 导入 Vue 组件

import Vue from 'vue'



// 导入 App组件

import App from './components/App.vue'



// 创建一个 Vue 实例,使用 render 函数,渲染指定的组件

var vm = new Vue({

el: '#app',

render: c => c(App)

});

在使用webpack构建的Vue项目中使用模板对象?

  1. webpack.config.js中添加resolve属性:
1
2
3
4
5
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}

ES6中语法使用总结

  1. 使用 export defaultexport 导出模块中的成员; 对应ES5中的 module.exportsexport

  2. 使用 import ** from **import '路径' 还有 import {a, b} from '模块标识' 导入其他模块

  3. 使用箭头函数:(a, b)=> { return a-b; }

在vue组件页面中,集成vue-router路由模块

vue-router官网

  1. 导入路由模块:
1
import VueRouter from 'vue-router'
  1. 安装路由模块:
1
Vue.use(VueRouter);
  1. 导入需要展示的组件:
1
2
3
import login from './components/account/login.vue'

import register from './components/account/register.vue'
  1. 创建路由对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
var router = new VueRouter({

routes: [

{ path: '/', redirect: '/login' },

{ path: '/login', component: login },

{ path: '/register', component: register }

]

});
  1. 将路由对象,挂载到 Vue 实例上:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var vm = new Vue({

el: '#app',

// render: c => { return c(App) }

render(c) {

return c(App);

},

router // 将路由对象,挂载到 Vue 实例上

});
  1. 改造App.vue组件,在 template 中,添加router-linkrouter-view
1
2
3
4
5
6
7
<router-link to="/login">登录</router-link>

<router-link to="/register">注册</router-link>



<router-view></router-view>

组件中的css作用域问题

抽离路由为单独的模块

使用 饿了么的 MintUI 组件

Github 仓储地址

Mint-UI官方文档

  1. 导入所有MintUI组件:
1
import MintUI from 'mint-ui'
  1. 导入样式表:
1
import 'mint-ui/lib/style.css'
  1. 在 vue 中使用 MintUI中的Button按钮和Toast弹框提示:
1
Vue.use(MintUI)
  1. 使用的例子:
1
<mt-button type="primary" size="large">primary</mt-button>

Mint-UI中按需导入的配置方式

使用 MUI 代码片段

注意: MUI 不同于 Mint-UI,MUI只是开发出来的一套好用的代码片段,里面提供了配套的样式、配套的HTML代码段,类似于 Bootstrap; 而 Mint-UI,是真正的组件库,是使用 Vue 技术封装出来的 成套的组件,可以无缝的和 VUE项目进行集成开发;
因此,从体验上来说, Mint-UI体验更好,因为这是别人帮我们开发好的现成的Vue组件;
从体验上来说, MUI和Bootstrap类似;
理论上,任何项目都可以使用 MUI 或 Bootstrap,但是,MInt-UI只适用于Vue项目;

注意: MUI 并不能使用 npm 去下载,需要自己手动从 github 上,下载现成的包,自己解压出来,然后手动拷贝到项目中使用;

官网首页

文档地址

  1. 导入 MUI 的样式表:
1
import '../lib/mui/css/mui.min.css'
  1. webpack.config.js中添加新的loader规则:
1
{ test: /\.(png|jpg|gif|ttf)$/, use: 'url-loader' }
  1. 根据官方提供的文档和example,尝试使用相关的组件

将项目源码托管到oschina中

  1. 点击头像 -> 修改资料 -> SSH公钥 如何生成SSH公钥

  2. 创建自己的空仓储,使用 git config --global user.name "用户名"git config --global user.email ***@**.com 来全局配置提交时用户的名称和邮箱

  3. 使用 git init 在本地初始化项目

  4. 使用 touch README.mdtouch .gitignore 来创建项目的说明文件和忽略文件;

  5. 使用 git add . 将所有文件托管到 git 中

  6. 使用 git commit -m "init project" 将项目进行本地提交

  7. 使用 git remote add origin 仓储地址将本地项目和远程仓储连接,并使用origin最为远程仓储的别名

  8. 使用 git push -u origin master 将本地代码push到仓储中

App.vue 组件的基本设置

  1. 头部的固定导航栏使用 Mint-UIHeader 组件;

  2. 底部的页签使用 muitabbar;

  3. 购物车的图标,使用 icons-extra 中的 mui-icon-extra mui-icon-extra-cart,同时,应该把其依赖的字体图标文件 mui-icons-extra.ttf,复制到 fonts 目录下!

  4. 将底部的页签,改造成 router-link 来实现单页面的切换;

  5. Tab Bar 路由激活时候设置高亮的两种方式:

    • 全局设置样式如下:
    1
    2
    3
    4
    5
    .router-link-active{

    color:#007aff !important;

    }
    • 或者在 new VueRouter 的时候,通过 linkActiveClass 来指定高亮的类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 创建路由对象

    var router = new VueRouter({

    routes: [

    { path: '/', redirect: '/home' }

    ],

    linkActiveClass: 'mui-active'

    });

实现 tabbar 页签不同组件页面的切换

  1. 将 tabbar 改造成 router-link 形式,并指定每个连接的 to 属性;

  2. 在入口文件中导入需要展示的组件,并创建路由对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 导入需要展示的组件

import Home from './components/home/home.vue'

import Member from './components/member/member.vue'

import Shopcar from './components/shopcar/shopcar.vue'

import Search from './components/search/search.vue'



// 创建路由对象

var router = new VueRouter({

routes: [

{ path: '/', redirect: '/home' },

{ path: '/home', component: Home },

{ path: '/member', component: Member },

{ path: '/shopcar', component: Shopcar },

{ path: '/search', component: Search }

],

linkActiveClass: 'mui-active'

});

使用 mt-swipe 轮播图组件

  1. 假数据:
1
2
3
4
5
6
7
8
9
lunbo: [

'http://www.itcast.cn/images/slidead/BEIJING/2017440109442800.jpg',

'http://www.itcast.cn/images/slidead/BEIJING/2017511009514700.jpg',

'http://www.itcast.cn/images/slidead/BEIJING/2017421414422600.jpg'

]
  1. 引入轮播图组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Mint-UI 轮播图组件 -->

<div class="home-swipe">

<mt-swipe :auto="4000">

<mt-swipe-item v-for="(item, i) in lunbo" :key="i">

<img :src="item" alt="">

</mt-swipe-item>

</mt-swipe>

</div>

</div>

.vue组件中使用vue-resource获取数据

  1. 运行cnpm i vue-resource -S安装模块

  2. 导入 vue-resource 组件

1
import VueResource from 'vue-resource'
  1. 在vue中使用 vue-resource 组件

Vue.use(VueResource);

使用mui的tab-top-webview-main完成分类滑动栏

兼容问题

  1. 和 App.vue 中的 router-link 身上的类名 mui-tab-item 存在兼容性问题,导致tab栏失效,可以把mui-tab-item改名为mui-tab-item1,并复制相关的类样式,来解决这个问题;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.mui-bar-tab .mui-tab-item1.mui-active {
color: #007aff;
}

.mui-bar-tab .mui-tab-item1 {
display: table-cell;
overflow: hidden;
width: 1%;
height: 50px;
text-align: center;
vertical-align: middle;
white-space: nowrap;
text-overflow: ellipsis;
color: #929292;
}

.mui-bar-tab .mui-tab-item1 .mui-icon {
top: 3px;
width: 24px;
height: 24px;
padding-top: 0;
padding-bottom: 0;
}

.mui-bar-tab .mui-tab-item1 .mui-icon~.mui-tab-label {
font-size: 11px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
  1. tab-top-webview-main组件第一次显示到页面中的时候,无法被滑动的解决方案:

    • 先导入 mui 的JS文件:
    1
    import mui from '../../../lib/mui/js/mui.min.js'
    • 在 组件的 mounted 事件钩子中,注册 mui 的滚动事件:
    1
    2
    3
    4
    5
    6
    mounted() {
    // 需要在组件的 mounted 事件钩子中,注册 mui 的 scroll 滚动事件
    mui('.mui-scroll-wrapper').scroll({
    deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
    });
    }
  2. 滑动的时候报警告:Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

1
解决方法,可以加上* { touch-action: none; } 这句样式去掉。

原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西) http://www.cnblogs.com/pearl07/p/6589114.html
https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action

移除严格模式

babel-plugin-transform-remove-strict-mode

vue-preview

一个Vue集成PhotoSwipe图片预览插件

在网页中会引用哪些常见的静态资源?

  • JS

    • .js .jsx .coffee .ts(TypeScript 类 C# 语言)
  • CSS

    • .css .less .sass .scss
  • Images

    • .jpg .png .gif .bmp .svg
  • 字体文件(Fonts)

    • .svg .ttf .eot .woff .woff2
  • 模板文件

    • .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】

网页中引入的静态资源多了以后有什么问题???

  1. 网页加载速度慢, 因为 我们要发起很多的二次请求;
  2. 要处理错综复杂的依赖关系

如何解决上述两个问题

  1. 合并、压缩、精灵图、图片的Base64编码
  2. 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系;

什么是webpack?

webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;

如何完美实现上述的2种解决方案

  1. 使用Gulp, 是基于 task 任务的;
  2. 使用Webpack, 是基于整个项目进行构建的;
  • 借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。
  • 根据官网的图片介绍webpack打包的过程
  • webpack官网

webpack安装的两种方式

  1. 运行npm i webpack -g全局安装webpack,这样就能在全局使用webpack的命令
  2. 在项目根目录中运行npm i webpac
  3. k --save-dev安装到项目依赖中

初步使用webpack打包构建列表隔行变色案例

  1. 运行npm init初始化项目,使用npm管理项目中的依赖包
  2. 创建项目基本的目录结构
  3. 使用cnpm i jquery --save安装jquery类库
  4. 创建main.js并书写各行变色的代码逻辑:
1
2
3
4
5
6
7
// 导入jquery类库
import $ from 'jquery'

// 设置偶数行背景色,索引从0开始,0是偶数
$('#list li:even').css('backgroundColor','lightblue');
// 设置奇数行背景色
$('#list li:odd').css('backgroundColor','pink');
  1. 直接在页面上引用main.js会报错,因为浏览器不认识import这种高级的JS语法,需要使用webpack进行处理,webpack默认会把这种高级的语法转换为低级的浏览器能识别的语法;
  2. 运行webpack 入口文件路径 输出文件路径main.js进行处理:
1
webpack src/js/main.js dist/bundle.js

使用webpack的配置文件简化打包时候的命令

  1. 在项目根目录中创建webpack.config.js
  2. 由于运行webpack命令的时候,webpack需要指定入口文件和输出文件的路径,所以,我们需要在webpack.config.js中配置这两个路径:
1
2
3
4
5
6
7
8
9
10
11
// 导入处理路径的模块
var path = require('path');

// 导出一个配置对象,将来webpack在启动的时候,会默认来查找webpack.config.js,并读取这个文件中导出的配置对象,来进行打包处理
module.exports = {
entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: 'bundle.js' // 配置输出的文件名
}
}

实现webpack的实时打包构建

  1. 由于每次重新修改代码之后,都需要手动运行webpack打包的命令,比较麻烦,所以使用webpack-dev-server来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。

  2. 运行cnpm i webpack-dev-server --save-dev安装到开发依赖

  3. 安装完成之后,在命令行直接运行webpack-dev-server来进行打包,发现报错,此时需要借助于package.json文件中的指令,来进行运行webpack-dev-server命令,在scripts节点下新增"dev": "webpack-dev-server"指令,发现可以进行实时打包,但是dist目录下并没有生成bundle.js文件,这是因为webpack-dev-server将打包好的文件放在了内存中

    • bundle.js放在内存中的好处是:由于需要实时打包编译,所以放在内存中速度会非常快
    • 这个时候访问webpack-dev-server启动的http://localhost:8080/网站,发现是一个文件夹的面板,需要点击到src目录下,才能打开我们的index首页,此时引用不到bundle.js文件,需要修改index.html中script的src属性为:<script src="../bundle.js"></script>
    • 为了能在访问http://localhost:8080/的时候直接访问到index首页,可以使用--contentBase src指令来修改dev指令,指定启动的根目录:
    1
    "dev": "webpack-dev-server --contentBase src"

    同时修改index页面中script的src属性为<script src="bundle.js"></script>

使用html-webpack-plugin插件配置启动页面

由于使用--contentBase指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin插件配置启动页面.

  1. 运行cnpm i html-webpack-plugin --save-dev安装到开发依赖
  2. 修改webpack.config.js配置文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 导入处理路径的模块
var path = require('path');
// 导入自动生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: 'bundle.js' // 配置输出的文件名
},
plugins:[ // 添加plugins节点配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路径
filename:'index.html'//自动生成的HTML文件的名称
})
]
}
  1. 修改package.jsonscript节点中的dev指令如下:
1
"dev": "webpack-dev-server"
  1. 将index.html中script标签注释掉,因为html-webpack-plugin插件会自动把bundle.js注入到index.html页面中!

实现自动打开浏览器、热更新和配置浏览器的默认端口号

注意:热更新在JS中表现的不明显,可以从一会儿要讲到的CSS身上进行介绍说明!

方式1:

  • 修改package.json的script节点如下,其中--open表示自动打开浏览器,--port 4321表示打开的端口号为4321,--hot表示启用浏览器热更新:
1
"dev": "webpack-dev-server --hot --port 4321 --open"

方式2:

  1. 修改webpack.config.js文件,新增devServer节点如下:
1
2
3
4
5
devServer:{
hot:true,
open:true,
port:4321
}
  1. 在头部引入webpack模块:
1
var webpack = require('webpack');
  1. plugins节点下新增:
1
new webpack.HotModuleReplacementPlugin()

使用webpack打包css文件

  1. 运行cnpm i style-loader css-loader --save-dev
  2. 修改webpack.config.js这个配置文件:
1
2
3
4
5
module: { // 用来配置第三方loader模块的
rules: [ // 文件的匹配规则
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }//处理css文件的规则
]
}
  1. 注意:use表示使用哪些模块来处理test所匹配到的文件;use中相关loader模块的调用顺序是从后向前调用的;

使用webpack打包less文件

  1. 运行cnpm i less-loader less -D
  2. 修改webpack.config.js这个配置文件:
1
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },

使用webpack打包sass文件

  1. 运行cnpm i sass-loader node-sass --save-dev
  2. webpack.config.js中添加处理sass文件的loader模块:
1
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }

使用webpack处理css中的路径

  1. 运行cnpm i url-loader file-loader --save-dev
  2. webpack.config.js中添加处理url路径的loader模块:
1
{ test: /\.(png|jpg|gif)$/, use: 'url-loader' }
  1. 可以通过limit指定进行base64编码的图片大小;只有小于指定字节(byte)的图片才会进行base64编码:
1
{ test: /\.(png|jpg|gif)$/, use: 'url-loader?limit=43960' },

使用babel处理高级JS语法

  1. 运行cnpm i babel-core babel-loader babel-plugin-transform-runtime --save-dev安装babel的相关loader包
  2. 运行cnpm i babel-preset-es2015 babel-preset-stage-0 --save-dev安装babel转换的语法
  3. webpack.config.js中添加相关loader模块,其中需要注意的是,一定要把node_modules文件夹添加到排除项:
1
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
  1. 在项目根目录中添加.babelrc文件,并修改这个配置文件如下:
1
2
3
4
{
"presets":["es2015", "stage-0"],
"plugins":["transform-runtime"]
}
  1. 注意:语法插件babel-preset-es2015可以更新为babel-preset-env,它包含了所有的ES相关的语法;

相关文章

babel-preset-env:你需要的唯一Babel插件
Runtime transform 运行时编译es6

开启Apache的gzip压缩

要让apache支持gzip功能,要用到deflate_Module和headers_Module。打开apache的配置文件httpd.conf,大约在105行左右,找到以下两行内容:(这两行不是连续在一起的)

1
2
#LoadModule deflate_module modules/mod_deflate.so
#LoadModule headers_module modules/mod_headers.so

然后将其前面的“#”注释删掉,表示开启gzip压缩功能。开启以后还需要进行相关配置。在httpd.conf文件的最后添加以下内容即可:

1
2
3
4
5
<IfModule deflate_module>
#必须的,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩
SetOutputFilter DEFLATE
DeflateCompressionLevel 9
</IfModule>

最少需要加上以上内容,才可以生gzip功能生效。由于没有做其它的额外配置,所以其它相关的配置均使用Apache的默认设置。这里说一下参数“DeflateCompressionLevel”,它表示压缩级别,值从1到9,值越大表示压缩的越厉害。

使用ngrok将本机映射为一个外网的Web服务器

注意:由于默认使用的美国的服务器进行中间转接,所以访问速度炒鸡慢,访问时可启用FQ软件,提高网页打开速度!

vuex

概念:
vuex 是 Vue 配套的 公共数据管理工具,它可以把一些共享的数据,保存到 vuex 中,方便 整个程序中的任何组件直接获取或修改我们的公共数据;

相关文件

  1. URL中的hash(井号)

vue学习二

品牌管理案例

添加新品牌

删除品牌

根据条件筛选品牌

  1. 1.x 版本中的filterBy指令,在2.x中已经被废除:

filterBy - 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<tr v-for="item in list | filterBy searchName in 'name'">

<td>{{item.id}}</td>

<td>{{item.name}}</td>

<td>{{item.ctime}}</td>

<td>

<a href="#" @click.prevent="del(item.id)">删除</a>

</td>

</tr>
  1. 在2.x版本中手动实现筛选的方式
  • 筛选框绑定到 VM 实例中的 searchName 属性:
1
2
3
<hr> 输入筛选名称:

<input type="text" v-model="searchName">
  • 在使用 v-for 指令循环每一行数据的时候,不再直接 item in list,而是 in 一个 过滤的methods 方法,同时,把过滤条件searchName传递进去:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<tbody>

<tr v-for="item in search(searchName)">

<td>{{item.id}}</td>

<td>{{item.name}}</td>

<td>{{item.ctime}}</td>

<td>

<a href="#" @click.prevent="del(item.id)">删除</a>

</td>

</tr>

</tbody>
  • search 过滤方法中,使用 数组的 filter 方法进行过滤:
1
2
3
4
5
6
7
8
9
search(name) {

return this.list.filter(x => {

return x.name.indexOf(name) != -1;

});

}

Vue调试工具vue-devtools的安装步骤和使用

Vue.js devtools - 翻墙安装方式 - 推荐

过滤器

概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

私有过滤器

  1. HTML元素:
1
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
  1. 私有 filters 定义方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用

dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错

var dt = new Date(input);

// 获取年月日

var y = dt.getFullYear();

var m = (dt.getMonth() + 1).toString().padStart(2, '0');

var d = dt.getDate().toString().padStart(2, '0');



// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

// 否则,就返回 年-月-日 时:分:秒

if (pattern.toLowerCase() === 'yyyy-mm-dd') {

return `${y}-${m}-${d}`;

} else {

// 获取时分秒

var hh = dt.getHours().toString().padStart(2, '0');

var mm = dt.getMinutes().toString().padStart(2, '0');

var ss = dt.getSeconds().toString().padStart(2, '0');



return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

}

}

}

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;

全局过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 定义一个全局过滤器

Vue.filter('dataFormat', function (input, pattern = '') {

var dt = new Date(input);

// 获取年月日

var y = dt.getFullYear();

var m = (dt.getMonth() + 1).toString().padStart(2, '0');

var d = dt.getDate().toString().padStart(2, '0');



// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

// 否则,就返回 年-月-日 时:分:秒

if (pattern.toLowerCase() === 'yyyy-mm-dd') {

return `${y}-${m}-${d}`;

} else {

// 获取时分秒

var hh = dt.getHours().toString().padStart(2, '0');

var mm = dt.getMinutes().toString().padStart(2, '0');

var ss = dt.getSeconds().toString().padStart(2, '0');



return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

}

});

注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

键盘修饰符以及自定义键盘修饰符

1.x中自定义键盘修饰符【了解即可】

1
Vue.directive('on').keyCodes.f2 = 113;

2.x中自定义键盘修饰符

  1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名:
1
Vue.config.keyCodes.f2 = 113;
  1. 使用自定义的按键修饰符:
1
<input type="text" v-model="name" @keyup.f2="add">

自定义指令

  1. 自定义全局和局部的 自定义指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 自定义全局指令 v-focus,为绑定的元素自动获取焦点:

Vue.directive('focus', {

inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用

el.focus();

}

});



// 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:

directives: {

color: { // 为元素设置指定的字体颜色

bind(el, binding) {

el.style.color = binding.value;

}

},

'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数

el.style.fontWeight = binding2.value;

}

}
  1. 自定义指令的使用方式:
1
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">

Vue 1.x 中 自定义元素指令【已废弃,了解即可】

1
2
3
4
5
Vue.elementDirective('red-color', {
bind: function () {
this.el.style.color = 'red';
}
});

使用方式:

1
<red-color>1232</red-color>

vue实例的生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!

  • 生命周期钩子:就是生命周期事件的别名而已;

  • 生命周期钩子 = 生命周期函数 = 生命周期事件

  • 主要的生命周期函数分类:

    • 创建期间的生命周期函数:

      • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
      • created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
      • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
      • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
    • 运行期间的生命周期函数:

    • beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
    • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
    • 销毁期间的生命周期函数:
    • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
    • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

lifecycle

vue-resource 实现 get, post, jsonp请求

除了 vue-resource 之外,还可以使用 axios 的第三方包实现实现数据的请求

  1. 之前的学习中,如何发起数据请求?

  2. 常见的数据请求类型? get post jsonp

  3. 测试的URL请求资源地址:

  4. JSONP的实现原理

    • 由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的 数据接口,浏览器认为这种访问不安全;
    • 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓,JSONP只支持Get请求);
    • 具体实现过程:
    • 先在客户端定义一个回调方法,预定义对数据的操作;
    • 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口;
    • 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;
    • 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了;
    • 带大家通过 Node.js ,来手动实现一个JSONP的请求例子;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    const http = require('http');
    // 导入解析 URL 地址的核心模块
    const urlModule = require('url');

    const server = http.createServer();
    // 监听 服务器的 request 请求事件,处理每个请求
    server.on('request', (req, res) => {
    const url = req.url;

    // 解析客户端请求的URL地址
    var info = urlModule.parse(url, true);

    // 如果请求的 URL 地址是 /getjsonp ,则表示要获取JSONP类型的数据
    if (info.pathname === '/getjsonp') {
    // 获取客户端指定的回调函数的名称
    var cbName = info.query.callback;
    // 手动拼接要返回给客户端的数据对象
    var data = {
    name: 'zs',
    age: 22,
    gender: '男',
    hobby: ['吃饭', '睡觉', '运动']
    }
    // 拼接出一个方法的调用,在调用这个方法的时候,把要发送给客户端的数据,序列化为字符串,作为参数传递给这个调用的方法:
    var result = `${cbName}(${JSON.stringify(data)})`;
    // 将拼接好的方法的调用,返回给客户端去解析执行
    res.end(result);
    } else {
    res.end('404');
    }
    });

    server.listen(3000, () => {
    console.log('server running at http://127.0.0.1:3000');
    });
  5. vue-resource 的配置步骤:

    • 直接在页面中,通过script标签,引入 vue-resource 的脚本文件;
    • 注意:引用的先后顺序是:先引用 Vue 的脚本文件,再引用 vue-resource 的脚本文件;
  6. 发送get请求:

1
2
3
4
5
getInfo() { // get 方式获取数据
this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
console.log(res.body);
})
}
  1. 发送post请求:
1
2
3
4
5
6
7
8
9
10
postInfo() {
var url = 'http://127.0.0.1:8899/api/post';
// post 方法接收三个参数:
// 参数1: 要请求的URL地址
// 参数2: 要发送的数据对象
// 参数3: 指定post提交的编码类型为 application/x-www-form-urlencoded
this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {
console.log(res.body);
});
}
  1. 发送JSONP请求获取数据:
1
2
3
4
5
6
jsonpInfo() { // JSONP形式从服务器获取数据
var url = 'http://127.0.0.1:8899/api/jsonp';
this.$http.jsonp(url).then(res => {
console.log(res.body);
});
}

配置本地数据库和数据接口API

  1. 先解压安装 PHPStudy;
  2. 解压安装 Navicat 这个数据库可视化工具,并激活;
  3. 打开 Navicat 工具,新建空白数据库,名为 dtcmsdb4;
  4. 双击新建的数据库,连接上这个空白数据库,在新建的数据库上右键 -> 运行SQL文件,选择并执行 dtcmsdb4.sql 这个数据库脚本文件;如果执行不报错,则数据库导入完成;
  5. 进入文件夹 vuecms3_nodejsapi 内部,执行 npm i 安装所有的依赖项;
  6. 先确保本机安装了 nodemon, 没有安装,则运行 npm i nodemon -g 进行全局安装,安装完毕后,进入到 vuecms3_nodejsapi目录 -> src目录 -> 双击运行 start.bat
  7. 如果API启动失败,请检查 PHPStudy 是否正常开启,同时,检查 app.js 中第 14行 中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root

品牌管理改造

展示品牌列表

添加品牌数据

删除品牌数据

Vue中的动画

为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;

使用过渡类名

  1. HTML结构:
1
2
3
4
5
6
7
<div id="app">
<input type="button" value="动起来" @click="myAnimate">
<!-- 使用 transition 将需要过渡的元素包裹起来 -->
<transition name="fade">
<div v-show="isshow">动画哦</div>
</transition>
</div>
  1. VM 实例:
1
2
3
4
5
6
7
8
9
10
11
12
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
isshow: false
},
methods: {
myAnimate() {
this.isshow = !this.isshow;
}
}
});
  1. 定义两组类样式:
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 定义进入和离开时候的过渡状态 */
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s ease;
position: absolute;
}

/* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(100px);
}

使用第三方 CSS 动画库

  1. 导入动画类库:
1
<link rel="stylesheet" type="text/css" href="./lib/animate.css">
  1. 定义 transition 及属性:
1
2
3
4
5
6
<transition
enter-active-class="fadeInRight"
leave-active-class="fadeOutRight"
:duration="{ enter: 500, leave: 800 }">
<div class="animated" v-show="isshow">动画哦</div>
</transition>

使用动画钩子函数

  1. 定义 transition 组件以及三个钩子函数:
1
2
3
4
5
6
7
8
9
<div id="app">
<input type="button" value="切换动画" @click="isshow = !isshow">
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div v-if="isshow" class="show">OK</div>
</transition>
</div>
  1. 定义三个 methods 钩子方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
methods: {
beforeEnter(el) { // 动画进入之前的回调
el.style.transform = 'translateX(500px)';
},
enter(el, done) { // 动画进入完成时候的回调
el.offsetWidth;
el.style.transform = 'translateX(0px)';
done();
},
afterEnter(el) { // 动画进入完成之后的回调
this.isshow = !this.isshow;
}
}/
  1. 定义动画过渡时长和样式:
1
2
3
.show{
transition: all 0.4s ease;
}

v-for 的列表过渡

  1. 定义过渡样式:
1
2
3
4
5
6
7
8
9
10
11
12
<style>
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateY(10px);
}

.list-enter-active,
.list-leave-active {
transition: all 0.3s ease;
}
</style>
  1. 定义DOM结构,其中,需要使用 transition-group 组件把v-for循环的列表包裹起来:
1
2
3
4
5
6
7
<div id="app">
<input type="text" v-model="txt" @keyup.enter="add">

<transition-group tag="ul" name="list">
<li v-for="(item, i) in list" :key="i">{{item}}</li>
</transition-group>
</div>
  1. 定义 VM中的结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
txt: '',
list: [1, 2, 3, 4]
},
methods: {
add() {
this.list.push(this.txt);
this.txt = '';
}
}
});

列表的排序过渡

<transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用

  • v-movev-leave-active 结合使用,能够让列表的过渡更加平缓柔和:
1
2
3
4
5
6
.v-move{
transition: all 0.8s ease;
}
.v-leave-active{
position: absolute;
}

相关文章

  1. vue.js 1.x 文档
  2. vue.js 2.x 文档
  3. String.prototype.padStart(maxLength, fillString)
  4. js 里面的键盘事件对应的键码
  5. pagekit/vue-resource
  6. navicat如何导入sql文件和导出sql文件
  7. 贝塞尔在线生成器

vue学习一

Vue.js - Day1

什么是Vue.js

  • Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)

  • Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架!

  • Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)

  • 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;

为什么要学习流行框架

  • 企业为了提高开发效率:在企业中,时间就是效率,效率就是金钱;

    • 企业中,使用框架,能够提高开发的效率;
  • 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)
  • 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;
  • 增强自己就业时候的竞争力

    • 人无我有,人有我优
    • 你平时不忙的时候,都在干嘛?

框架和库的区别

  • 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。

    • node 中的 express;
  • 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。

      1. 从Jquery 切换到 Zepto
      1. 从 EJS 切换到 art-template

Node(后端)中的 MVC 与 前端中的 MVVM 之间的区别

  • MVC 是后端的分层开发概念;

  • MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel

  • 为什么有了MVC还要有MVVM

01.MVC和MVVM的关系图解

Vue.js 基本代码 和 MVVM 之间的对应关系

Vue之 - 基本的代码结构插值表达式v-cloak

Vue指令之v-textv-html

Vue指令之v-bind的三种用法

  1. 直接使用指令v-bind

  2. 使用简化指令:

  3. 在绑定的时候,拼接绑定内容::title="btnTitle + ', 这是追加的内容'"

Vue指令之v-on跑马灯效果

跑马灯效果

  1. HTML结构:
1
2
3
4
5
6
7
8
9
<div id="app">

<p>{{info}}</p>

<input type="button" value="开启" v-on:click="go">

<input type="button" value="停止" v-on:click="stop">

</div>
  1. Vue实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 创建 Vue 实例,得到 ViewModel

var vm = new Vue({

el: '#app',

data: {

info: '猥琐发育,别浪~!',

intervalId: null

},

methods: {

go() {

// 如果当前有定时器在运行,则直接return

if (this.intervalId != null) {

return;

}

// 开始定时器

this.intervalId = setInterval(() => {

this.info = this.info.substring(1) + this.info.substring(0, 1);

}, 500);

},

stop() {

clearInterval(this.intervalId);

}

}

});

Vue指令之v-on的缩写事件修饰符

事件修饰符:

  • .stop 阻止冒泡

  • .prevent 阻止默认事件

  • .capture 添加事件侦听器时使用事件捕获模式

  • .self 只当事件在该元素本身(比如不是子元素)触发时触发回调

  • .once 事件只触发一次

Vue指令之v-model双向数据绑定

简易计算器案例

  1. HTML 代码结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">

<input type="text" v-model="n1">

<select v-model="opt">

<option value="0">+</option>

<option value="1">-</option>

<option value="2">*</option>

<option value="3">÷</option>

</select>

<input type="text" v-model="n2">

<input type="button" value="=" v-on:click="getResult">

<input type="text" v-model="result">

</div>
  1. Vue实例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 创建 Vue 实例,得到 ViewModel

var vm = new Vue({

el: '#app',

data: {

n1: 0,

n2: 0,

result: 0,

opt: '0'

},

methods: {

getResult() {

switch (this.opt) {

case '0':

this.result = parseInt(this.n1) + parseInt(this.n2);

break;

case '1':

this.result = parseInt(this.n1) - parseInt(this.n2);

break;

case '2':

this.result = parseInt(this.n1) * parseInt(this.n2);

break;

case '3':

this.result = parseInt(this.n1) / parseInt(this.n2);

break;

}

}

}

});

在Vue中使用样式

使用class样式

  1. 数组
1
<h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
  1. 数组中使用三元表达式
1
<h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
  1. 数组中嵌套对象
1
<h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
  1. 直接使用对象
1
<h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>

使用内联样式

  1. 直接在元素上通过 :style 的形式,书写样式对象
1
<h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
  1. 将样式对象,定义到 data 中,并直接引用到 :style

    • 在data上定义样式:
1
2
3
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
  • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
1
<h1 :style="h1StyleObj">这是一个善良的H1</h1>
  1. :style 中通过数组,引用多个 data 上的样式对象

    • 在data上定义样式:
1
2
3
4
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
  • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
1
<h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>

Vue指令之v-forkey属性

  1. 迭代数组
1
2
3
<ul>
<li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年龄:{{item.age}}</li>
</ul>
  1. 迭代对象中的属性
1
2
3
<!-- 循环遍历对象身上的属性 -->

<div v-for="(val, key, i) in userInfo">{{val}} --- {{key}} --- {{i}}</div>
  1. 迭代数字
1
<p v-for="i in 10">这是第 {{i}} 个P标签</p>

2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。

Vue指令之v-ifv-show

一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

品牌管理案例

添加新品牌

删除品牌

根据条件筛选品牌

  1. 1.x 版本中的filterBy指令,在2.x中已经被废除:

filterBy - 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<tr v-for="item in list | filterBy searchName in 'name'">

<td>{{item.id}}</td>

<td>{{item.name}}</td>

<td>{{item.ctime}}</td>

<td>

<a href="#" @click.prevent="del(item.id)">删除</a>

</td>

</tr>
  1. 在2.x版本中手动实现筛选的方式
  • 筛选框绑定到 VM 实例中的 searchName 属性:
1
2
3
<hr> 输入筛选名称:

<input type="text" v-model="searchName">
  • 在使用 v-for 指令循环每一行数据的时候,不再直接 item in list,而是 in 一个 过滤的methods 方法,同时,把过滤条件searchName传递进去:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<tbody>

<tr v-for="item in search(searchName)">

<td>{{item.id}}</td>

<td>{{item.name}}</td>

<td>{{item.ctime}}</td>

<td>

<a href="#" @click.prevent="del(item.id)">删除</a>

</td>

</tr>

</tbody>
  • search 过滤方法中,使用 数组的 filter 方法进行过滤:
1
2
3
4
5
6
7
8
9
search(name) {

return this.list.filter(x => {

return x.name.indexOf(name) != -1;

});

}

Vue调试工具vue-devtools的安装步骤和使用

Vue.js devtools - 翻墙安装方式 - 推荐

过滤器

概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

私有过滤器

  1. HTML元素:
1
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
  1. 私有 filters 定义方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用

dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错

var dt = new Date(input);

// 获取年月日

var y = dt.getFullYear();

var m = (dt.getMonth() + 1).toString().padStart(2, '0');

var d = dt.getDate().toString().padStart(2, '0');



// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

// 否则,就返回 年-月-日 时:分:秒

if (pattern.toLowerCase() === 'yyyy-mm-dd') {

return `${y}-${m}-${d}`;

} else {

// 获取时分秒

var hh = dt.getHours().toString().padStart(2, '0');

var mm = dt.getMinutes().toString().padStart(2, '0');

var ss = dt.getSeconds().toString().padStart(2, '0');



return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

}

}

}

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;

全局过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 定义一个全局过滤器

Vue.filter('dataFormat', function (input, pattern = '') {

var dt = new Date(input);

// 获取年月日

var y = dt.getFullYear();

var m = (dt.getMonth() + 1).toString().padStart(2, '0');

var d = dt.getDate().toString().padStart(2, '0');



// 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

// 否则,就返回 年-月-日 时:分:秒

if (pattern.toLowerCase() === 'yyyy-mm-dd') {

return `${y}-${m}-${d}`;

} else {

// 获取时分秒

var hh = dt.getHours().toString().padStart(2, '0');

var mm = dt.getMinutes().toString().padStart(2, '0');

var ss = dt.getSeconds().toString().padStart(2, '0');



return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

}

});

注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

键盘修饰符以及自定义键盘修饰符

1.x中自定义键盘修饰符【了解即可】

1
Vue.directive('on').keyCodes.f2 = 113;

2.x中自定义键盘修饰符

  1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名:
1
Vue.config.keyCodes.f2 = 113;
  1. 使用自定义的按键修饰符:
1
<input type="text" v-model="name" @keyup.f2="add">

自定义指令

  1. 自定义全局和局部的 自定义指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 自定义全局指令 v-focus,为绑定的元素自动获取焦点:

Vue.directive('focus', {

inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用

el.focus();

}

});



// 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:

directives: {

color: { // 为元素设置指定的字体颜色

bind(el, binding) {

el.style.color = binding.value;

}

},

'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数

el.style.fontWeight = binding2.value;

}

}
  1. 自定义指令的使用方式:
1
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">

Vue 1.x 中 自定义元素指令【已废弃,了解即可】

1
2
3
4
5
Vue.elementDirective('red-color', {
bind: function () {
this.el.style.color = 'red';
}
});

使用方式:

1
<red-color>1232</red-color>

相关文章

  1. vue.js 1.x 文档
  2. vue.js 2.x 文档
  3. String.prototype.padStart(maxLength, fillString)
  4. js 里面的键盘事件对应的键码
  5. Vue.js双向绑定的实现原理

浅谈canvas和相关实战

涂鸦部分
  • 为了兼容性,使用canvas.getContext判断是否有该属性。
  • 使用canvas.getContext()获得画板执行上下文。
  • 给canvas添加鼠标按下事件。
    • 获取鼠标按下时,相对画布的x,y坐标。
    • 设置画笔颜色,以及笔的宽度(ctx.lineWidth);
    • 开始画线。
canvas的lineCap属性
  • lineCap属性是设置或返回线条末端线帽的样式,其中,“round”和“square”会使线条稍微变长。

  • context.lineCap="butt|round|square";

    描述
    butt 默认。向线条的每个末端添加平直的边缘。
    round 向线条的每个末端添加圆形线帽。
    square 向线条的每个末端添加正方形线帽。
globalCompositeOperation 属性
  • globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。

    源图像 = 您打算放置到画布上的绘图。

    目标图像 = 您已经放置在画布上的绘图。

  • context.globalCompositeOperation="source-in";在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas{
box-shadow: 0 0 10px black;
margin: 0 auto;
display: block;
margin-top: 30px;
}
</style>
</head>
<body>
<canvas width="400" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(20,20,100,100);

ctx.globalCompositeOperation = 'source-atop'
ctx.fillStyle = 'red';
ctx.fillRect(80,80,100,100);
</script>
请求关键帧运动
  • window.requestAnimationFrame(move);系统自动计算出什么时候执行该函数或方法。

  • 与setTimeout与setInterval一样设置定时运动,但window.requestAnimationFrame(move);更流畅,且丢帧情况比较少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>涂鸦</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
background-color: gray;
}
</style>
</head>

<body>
<canvas id="canvas" width="400" height="400"></canvas>
<button onclick="clear()">清除</button>
</body>

</html>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 画笔部分
canvas.onmousedown = function (e) {
var x, y;
x = e.clientX - canvas.offsetLeft;
y = e.clientY - canvas.offsetTop;
ctx.lineWidth = 20;
ctx.beginPath();
ctx.strokeStyle = 'blue';

ctx.moveTo(x, y);
canvas.onmousemove = function (e) {
ctx.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
ctx.stroke();
}
canvas.onmouseup = function () {
canvas.onmousemove = '';
}
}
</script>
图像水平移动
ctx.drawImage()
  • drawImage()方法能在画布上绘制图像,画布或视频,也能够绘制图像的某些部分,以及增加或减少图像的尺寸。
  • js语法
  1. 在画布上定位图像:ctx.drawImage(img,x,y);
  2. 在画布上定位图像并规定图像的宽高:ctx.drawImage(img,x,y,width,height);
  3. 剪切图像,并在画布上定位被剪切的部分:ctx.drawImage(img,sx,sy,swidth,sheigt,x,y,width,height);
参数 描述
img 规定要使用的图像、画布或视频。
sx 可选。开始剪切的 x 坐标位置。
sy 可选。开始剪切的 y 坐标位置。
swidth 可选。被剪切图像的宽度。
sheight 可选。被剪切图像的高度。
x 在画布上放置图像的 x 坐标位置。
y 在画布上放置图像的 y 坐标位置。
width 可选。要使用的图像的宽度(伸展或缩小图像)。
height 可选。要使用的图像的高度(伸展或缩小图像)。
1
2
3
4
5
6
7
8
//一般需要先指定图片属性
var img = new Image();
img.src = '*.png';

//图片加载完完才开始绘制
img.onload = function(){
ctx.drawImage(img,x,y);
}
ctx.createPattern()
  • ctx.createPattern()方法是在指定的方向内重复指定的元素。元素可以是图片,视频或其他canvas元素。

  • 被重复的元素可用于绘制/填充矩形,圆形或线条等等。

  • ctx.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat");

    参数 描述
    image 规定要使用的模式的图片、画布或视频元素。
    repeat 默认。该模式在水平和垂直方向重复。
    repeat-x 该模式只在水平方向重复。
    repeat-y 该模式只在垂直方向重复。
    no-repeat 该模式只显示一次(不重复)。
记录状态save()
  • 可以通过ctx.save()方法保存当前环境的状态,包括位置,画笔颜色等。

  • ctx.restore()则是还原之前画过的路径状态和属性。

    注意:save的存储方式类似于栈的存储结构;即第一次调用的状态是倒数第一次save的状态,第二次调用的状态是倒数第二次save的状态。

改变原点位置translate
  • translate() 方法重新映射画布上的 (0,0) 位置。

    注意:当您在 translate() 之后调用诸如 fillRect() 之类的方法时,值会添加到 x 和 y 坐标值上。

  • 在位置 (10,10) 处绘制一个矩形,将新的 (0,0) 位置设置为 (70,70)。再次绘制新的矩形(请注意现在矩形从位置 (80,80) 开始绘制)

项目

水平全景展示项目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas{
box-shadow: 0 0 10px #123;
display: block;
margin: 0 auto;
margin-top: 25px;
}
</style>
</head>
<body>
<canvas width="800" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
// 创建图片
var img = new Image();
img.src = '5.png';
img.onload = function(){
drawImage();
}
// 图片绘制开始点
var x = 0;
function drawImage(){
// 记录状态
ctx.save();
ctx.clearRect(0,0,canvas.width,canvas.height);
// 图片绘制开始点向左移动
ctx.translate(-x,0);
// 图片将画布铺满
ctx.drawImage(img,0,0);
// 为实现无缝连接再画一张图片
ctx.drawImage(img,canvas.width,0);
x++;
// 当第一张图片溢出画布时,就将x置为0.
if(x >= canvas.width){
x = 0;
}
// 还原之前画过的路径状态和属性。
ctx.restore();
// arguments.callee等价于drawImage
// 自动定时调用函数
window.requestAnimationFrame(arguments.callee);
}
</script>
球碰撞项目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
box-shadow: 0 0 10px black;
display: block;
margin: 0 auto;
margin-top: 30px;
}
</style>
</head>

<body>
<canvas width="600" height="400"></canvas>
</body>

</html>
<script>
// 获取canvas对象
var canvas = document.querySelector('canvas');
// 获取执行上下文
var ctx = canvas.getContext('2d');

// 建一个画⚪的类
function Circle(x, y, r, color, speed) {
this.x = x;
this.y = y;
this.r = r;
this.color = color;
this.speed = speed;
}

// 在原型上定义draw方法画圆
Circle.prototype.draw = function () {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
}

// 在原型上定义move移动
Circle.prototype.move = function(){
this.x += this.speed;
this.y += this.speed;
if(this.x < this.r || this.x + this.r > canvas.width){
this.speed *= -1;
}else if(this.x == this.r && this.y == this.r){
// 解决正好卡在对角的球
this.speed *= -1;
}
if(this.y < this.r || this.y + this.r > canvas.height){
this.speed *= -1;
}
}
var circle1 = new Circle(40,40, 20, 'red', 2);
var circle2 = new Circle(190,250, 20, 'blue', 2);
circle1.draw();
circle2.draw();
actions();
function actions(){
ctx.clearRect(0,0,canvas.width,canvas.height);
circle1.move();
circle2.move();
circle1.draw();
circle2.draw();
// 检测两个球是否相撞
if(isCircleHit(circle1,circle2)){
circle1.speed *= -1;
circle2.speed *= -1;
}
window.requestAnimationFrame(arguments.callee);
}
function isCircleHit(circle1,circle2){
var min1 = circle1.x;
var min2 = circle2.x;
var max1 = circle1.x + circle1.r;
var max2 = circle2.x + circle2.r;
var min = Math.min(min1.min2);
var max = Math.max(max1,max2);
if(min >= max){
return true;
}
return false;
}
</script>
刮刮卡项目

实现:1. 有一张图和一个画布。

  1. 给图片蒙上一张灰色蒙版,当鼠标按下时获取当前鼠标相对画布的坐标。
  2. 设置ctx.globalCompositeOperation 为 ‘destination-out’,在源图像(灰色蒙版)中显示目标图像(画笔)。只有源图像之内的目标图像部分会被显示,源图像是透明的。
  3. 在不松开鼠标前提下,移动鼠标,得到并不断更新的坐标,先清除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
position: absolute;
left: 0;
}
</style>
</head>

<body>
<img src="5.png" alt="Image" width="300" height="400">
<canvas width="300" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'gray';
ctx.fillRect(0, 0, 300, 400);
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 10;
ctx.lineCap = 'round';
canvas.onmousedown = function (e) {
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
canvas.onmousemove = function (e) {
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
}
canvas.onmouseup = function(e){
canvas.onmousemove = '';
}

}
</script>

setTimeout与setInterval区别

setTimeout与setInterval区别

这两个都是原生js的定时方法,但执行次数不同。

  • setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。简单得说就是几秒之后,你把我交给你得任务执行了。(执行一次)

    setTimeout(function(){ alert("Hello"); }, 3000);

  • setInterval()可按照指定的周期(以毫秒计)来调用函数或计算表达式,直到clearInterval()方法被调用或窗口被关闭。简单来说就是每几秒执行一次,无限执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script>
var w, h;
var canvas = document.querySelector('canvas');
~~function setSize() {
window.onresize = arguments.callee;
w = window.innerWidth;
h = window.innerHeight;
canvas.width = w;
canvas.height = h;
}();
var canCon = canvas.getContext('2d');
var x = randomNum(0,w);
var y = 100;
setInterval(function(){
canCon.fillStyle = randomColor();
canCon.arc(x,y++,20,0,2*Math.PI)
canCon.fill();
},1000/60);

// 随机数
function randomNum(min,max){
return Math.floor(Math.random() * (max - min) + min);
}
// 随机颜色
function randomColor(){
var r,g,b,a;
r = randomNum(0,255);
g = randomNum(0,255);
b = randomNum(0,255);
a = Math.random();
return `rgba(${r},${g},${b},${a})`;
}
</script>

至此一个闪动的滚动条出现。

但是我发现setInterval越来越快的问题…….

经过仔细排查之后,我排除了我自己代码的问题,于是就开始查各种资料。最后才晓得这是setInterval()自身的问题。下面引用大佬的一段话来解释为什么使用setInterval会出现越来越快的问题。

“JavaScript是运行在单线程的环境中的,所以这就意味着定时器就成了要执行的计划!而不是必须要执行的铁律! 为啥呢? 当函数开始执行时在栈中创建出来一个栈帧,这个栈帧的执行是需要时间的,假设有3秒,在这三秒内,JavaScript的单线程特点就会确保在这3秒内全力的专一的去解决掉这个栈帧(函数)。所以在这个函数运行的时候定时器是没有能力终止他的运行的,因此当函数的运行时间大于间隔时间时,间隔时间1秒到了,但是程序还有2秒没有执行完,那你也给我老老实实的等着函数执行完!!!”

知道了问题的原因,那我们再来看一下怎么去解决这个问题。

使用setTimeout循环来解决setInterval越来越快的问题

1
2
3
4
5
6
7
8
9
var i=0;
function show() {
console.log(i);
i++;
if(i<5){
setTimeout(show,1000);
}
}
show();

解决重复点击,setInterval越来越快问题

在data中定义intervalId存储定时器,初始化intetvalId为null,当触发事件时,在方法内部先判断intervalId是否为null,是则开启interval,否则不做处理返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<button @click="openInterval">开始</button>
<button @click="closeInterVal">关闭</button>

data(){
return {
intervalId: null
}
},
methods: {
openInterval(){
if(this.intervalId != null) return;
this.setintervalId = setInterval(() => {});
}
closeInterVal(){
clearInterval(this.intervalId);
this.intervalId = null;
}
}
完成实例

https://nikita-chuyin.github.io/2020/03/09/canvas%E7%BB%98%E5%88%B6%E7%A2%B0%E6%92%9E%E7%90%83%E5%8A%A8%E7%94%BB/

js中callee和caller

caller
  1. 函数fun的caller返回调用fun的函数对象,即fun的执行环境,如果fun的执行环境为window则返回null;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function fun(){
    console.log(argument.callee.caller);
    //argument.callee.caller必须写在fun中,
    //因为argument,caller只在执行时才有效。
    }
    fun();
    //结果为null

    function a(){
    fun();
    function fun(){
    console.log(fun.caller)//这里必须写在fun里面,因为caller只有函数执行过程中才有效
    }
    }
    a();

    结果为: a函数
    这里写图片描述

    js中callee关键字的作用
    1. callee关键字的定义:在函数内部使用,代表当前函数的引用(名字)。

    2. 作用:降低代码的耦合度。

    3. 耦合度的定义:一处代码的修改会导致其他代码也要发生改变(耦合度高)在项目里边要开发低耦合度的代码(一处代码修改尽量少地引起其他代码的变化)。

    4. 语法结构

      1
      2
      function f1(){arguments.callee();}
      f1();

    实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script type="text/javascript">
            function jiecheng(n){
                if(n==1){
                    return 1;
                }
                //return n * jiecheng(n-1);
                //callee可以保证外部名称的变化,不会引起内部代码的修改,代码耦合度降低
                return n * arguments.callee(n-1);
            }
            //要把jiecheng名称换成其他的名字进行使用

            var jc = jiecheng; //对象赋值,其为引用传递

            jiecheng = null;  //销毁jiecheng函数对象,后期不使用了
            console.log(jc(4)); //输出24
            var jd = jc;
            jc = null;
            console.log(jd(6)); //输出720
    </script>

    callee返回正在执行的函数本身的引用。callee是arguments的一个属性,这个属性是一个指针,指向这个拥有arguments对象的函数,(arguments是调用函数,那么这个callee就是调用函数的啦)

    完整项目实例:

    https://nikita-chuyin.github.io/2020/03/09/canvas%E7%BB%98%E5%88%B6%E7%A2%B0%E6%92%9E%E7%90%83%E5%8A%A8%E7%94%BB/

行内元素存在间距问题

行内元素之间存在间距的问题

行内元素不换行

刚在做项目的时候,发现发现,display:inline-block;的元素(即行内元素)之间存在一些间距。我查看之后发现没有margin,padding这种东西。即使我使用通配符也不能消除内外边距,究其原因是行内元素的回车换行符引起的。看看以下实例:

1
2
3
<input />
<button>提交</button>
<input type="text"><input type="submit">

img

可以看出相邻行内元素间当换行写时有间隔,不换行写时没有间隔。元素间留白间距出现的原因就是标签段之间的空格,因此,去掉HTML中的空格,自然间距就木有了。

故解决方法之一是行内元素在编辑器中不换行。但考虑到代码可读性,显然连成一行的写法是不可取的。

使用margin负值

我们还可以使用margin负值,但是margin负值的大小与上下文的字体和文字大小相关。

例如,对于12像素大小的上下文,Arial字体的margin负值为-3像素,Tahoma和Verdana就是-4像素,而Geneva为-6像素。

由于外部环境的不确定性,以及最后一个元素多出的父margin值等问题,这个方法不适合大规模使用。

不写闭合标签
1
2
3
4
5
6
7
8
9
10
11
12
<div class="space">
<a href="##">惆怅</a>
<a href="##">淡定</a>
<a href="##">热血</a>
</div>
<br>
<div class="space">
<a href="##">惆怅
<a href="##">淡定
<a href="##">热血</a>
</div>
/*为了兼容IE6/IE7的浏览器,最后一个列表的标签的结束(闭合)标签不能丢。*/

img

设置font-size为0;

添加样式

1
2
3
4
5
6
.space {
font-size: 0;
}
.space a {
font-size: 12px;
}

这个方法,基本上可以解决大部分浏览器下inline-block元素之间的间距(IE7等浏览器有时候会有1像素的间距)。但是随之而来的问题就是这个盒子里不能有文字了,其子元素可以设置字体需多嵌套一层。

使用letter-spacing
1
2
3
4
5
6
.space {
letter-spacing: -5px;
}
.space a {
letter-spacing: 0;
}

该方法可以搞定基本上所有浏览器。

使用word-spacing
1
2
3
4
5
6
.space {
word-spacing: -5px;
}
.space a {
word-spacing: 0;
}

一个是字符间距(letter-spacing)一个是单词间距(word-spacing),大同小异。

使用浮动

总结:

行内元素之间存在间距的问题解决方法:

  1. 行内元素不换行,但这样页面不够美观。
  2. 使用margin负值,这个得结合字体大小调整,不是特别建议使用。
  3. 不写闭合标签。
  4. 使用font-size:0px;这样也可以使间距消失,但是随之而来的问题就是这个盒子里不能有文字了,其子元素可以设置字体需多嵌套一层。
  5. 使用letter-spacing或word-spaing。
  6. 最后一个flot,flot:left。不建议使用,使用时注意清除浮动。

注:

最近还在用canvas绘图,想要实现设置画布宽高为屏幕宽高,但是页面出现了滚动条,而这也是行内元素的杰作。

1
2
3
4
5
 var w = window.innerWidth,
h = window.innerHeight;
var canvas = document.getElementById('canvas');
canvas.width = w;
canvas.height = h;

解决方法:

既然是行内元素引起的,那我就将元素设置为块级元素。同时去除默认边距。

1
2
3
4
5
6
7
8
*{
margin: 0;
padding: 0;
}
#canvas{
background-color: red;
display: block;
}

canvas绘制碰撞球动画

碰撞球动画
  1. 画一个简单的球

    1. 通过选择器获取画布。

      var canvas = document.getElementById('canvas');var canvas = document.querySelector('#canvas')

    2. 获取屏幕宽高,设置画布宽高为屏幕宽高,背景色。当浏览器宽高变化时重新给canvas元素设置宽高。

    1
    2
    出现问题:1. 页面出现滚动条。
    2. 宽高只是页面初始宽高,改变窗口大小时,画布不能随页面宽高切换。

    解决方法:

    1. 出现滚动条是因为canvas是行内元素(display:inline-block;),行内元素有行内行间距,故当你设置画布宽高为屏幕宽高,滚出滚动条那部分即为行间距。既然是行内元素引起的,那我就将元素设置为块级元素。同时去除默认边距。

      1
      2
      3
      4
      5
      6
      7
      8
      *{
      margin: 0;
      padding: 0;
      }
      #canvas{
      background-color: red;
      display: block;
      }
    2. 给函数添加reSize方法,当改变窗口大小时,给画布重新设置大小。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      var w, h;
      var canvas = document.querySelector('canvas');
      ~~function setSize() {
      window.onresize = arguments.callee;
      w = window.innerWidth;
      h = window.innerHeight;
      canvas.width = w;
      canvas.height = h;
      }();
      //立即执行函数写法之一,
      //还有(function(){}());
      //或(function(){})();
  2. 获取画布可画区域(getContext)

    1. 设置是2d效果还是3d效果。

      var canCon = canvas.getContext('2d');

  3. 设置用什么笔和笔的颜色。

    canCon.fillStyle = 'red';

  4. 构思图形。

    1. 提笔。(beginPath)

    2. 画什么形状,图形的位置,大小。

      canCon.arc(300,100,50,0,2*Math.PI)

  5. 开始画。

    1. 确定空心还是实心(stroke或fill)。

    2. 还是绘制文字。(fillText,strokeText)

      canCon.fill()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script>
var w, h;
var canvas = document.querySelector('canvas');
~~function setSize() {
window.onresize = arguments.callee;
w = window.innerWidth;
h = window.innerHeight;
canvas.width = w;
canvas.height = h;
}();
var canCon = canvas.getContext('2d');
var x = randomNum(0,w);
var y = 100;
setInterval(function(){
canCon.fillStyle = randomColor();
canCon.arc(x,y++,20,0,2*Math.PI)
canCon.fill();
},1000/60);

// 随机数
function randomNum(min,max){
return Math.floor(Math.random() * (max - min) + min);
}
// 随机颜色
function randomColor(){
var r,g,b,a;
r = randomNum(0,255);
g = randomNum(0,255);
b = randomNum(0,255);
a = Math.random();
return `rgba(${r},${g},${b},${a})`;
}
</script>

至此一个闪动的滚动条出现。

要出现很多球群魔乱舞现象,需要构造一个球的生成函数,里面包括初始化值,画球,还有球的移动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
canvas{
background-color: black;
display: block;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var w,h;
var aBubble = [];
~~function(){
window.onresize = arguments.callee;
w = window.innerWidth;
h = window.innerHeight;
canvas.width = w;
canvas.height = h;
}();
var canCon = canvas.getContext('2d');
function Bubble(){};
Bubble.prototype = {
init:function(){
this.r = random(4,6);
this.x = random(this.r,w);
this.y = random(this.r,h);
this.color = randomColor();
this.vx = random(-2,2);
this.vy = random(-1,1);
},
draw:function(){
canCon.beginPath();
canCon.fillStyle = this.color;
canCon.arc(this.x,this.y,this.r,0,Math.PI * 2);
canCon.fill();
},
move:function(){
this.x += this.vx;
this.y += this.vy;
if(this.x < this.r || this.x + this.r > w){
this.vx = -this.vx;
}
if(this.y < this.r || this.y + this.r > h){
this.vy = -this.vy;
}
this.draw();
}
}
function create(num){
for(var i = 0; i < num; i++){
var bubble = new Bubble();
bubble.init();
bubble.draw();
aBubble.push(bubble);
}
}
create(999);
setInterval(function(){
canCon.clearRect(0,0,w,h); //必须有这一句,不然贱泪横生
for(var item of aBubble){
item.move();
}
},1000/60);

// 随机数
function random(min, max){
return Math.floor(Math.random() * (max - min) + min);
}
// 随机颜色生成器
function randomColor(){
var r,g,b,a;
r = random(0,255);
g = random(0,255);
b = random(0,255);
a = Math.random();
return `rgba(${r},${g},${b},${a})`;
}
</script>

知识点:

  1. for … of …是遍历数组中的子项,for … in … 遍历下标。
  2. 创建一个数组来存放小球,用以标记已生成的小球。

用JavaScript封装栈

用JavaScript封装栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 封装栈
function Stack(){
// 栈中的属性
this.items = [];
// 栈中的相关操作
// 1.将元素压入栈中
Stack.prototype.push = function(element){
this.items.push(element);
};
// 2.将元素弹出栈中
Stack.prototype.pop = function(){
return this.items.pop();
};
// 3.查看一下栈顶元素
Stack.prototype.check = function(){
return this.items[this.items.length - 1];
};
// 4.判断栈中是否有元素
Stack.prototype.isEmpty = function(){
return this.items.length == 0;
};
// 5.获取栈中的元素的个数
Stack.prototype.size = function(){
return this.items.length;
};
// 6.toString方法
Stack.prototype.toString = function(){
// 方法一:
// var resultString = '';
// for(var i = 0; i < this.items.length; i ++){
// resultString += this.items[i] + ' ';
// }
// return resultString;
// 方法二:
return this.items.join(' ');
};
}

之所以不使用this.push=function(){},而是采用原型的方法,是因为通过prototype原型的方法相当于给整个类添加了方法,而this.push方式则仅是给某个实例添加方法。

用栈将十进制转为二进制

  • 因为我们习惯使用十进制,而计算机里面的所有内容都是用二进制数字表示的(0和1)
  • 可采用对十进制的数字进行除二取余法,将十进制转为二进制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//函数:将十进制转为二进制
function dec2bin(decNumber){
// 1.定义栈对象
var stack = new Stack();
while(decNumber > 0){
stack.push(decNumber % 2);
decNumber = Math.floor(decNumber / 2);
console.log(decNumber)
}
var binaryString = '';
while(!stack.isEmpty()){
binaryString += stack.pop();
}
return binaryString;
}

伪类与伪元素的区别

详解伪类与伪元素推荐:

https://www.cnblogs.com/slly/p/10239055.html

伪类与伪元素的区别

  1. 伪类本质上是为了弥补常规css选择器的不足,以便获取更多的信息。
  2. 伪元素本质上是创建一个有内容的虚拟容器
  3. CSS3中伪类和伪元素语法不同。
    1. 伪类 :link :hover
    2. 伪元素 ::before ::after
  4. 可以同时使用多个伪类,却只能同时使用一个伪元素。
  5. 其中伪元素和伪类的根本区别在于:他们是否创造了新的元素,这个新的元素就叫做“伪元素”
    1. 伪元素/伪对象:不存在在DOM文档中,是虚拟元素,是创建新元素。这个新元素是某个元素的子元素,这个元素虽然逻辑上存在,但却不存在于文档树中。
    2. 伪类:存在于DOM文档中,(无标签,只在触发条件时才能看到),逻辑上存在但在文档树上却无需标识的分类。
  6. 因为伪元素类似于添加类,故可添加多个,而伪元素在一个选择其中只能出现一次,并且只能出现在末尾。

伪元素单冒号与双冒号区别

相同点

  • 都可以用来表示伪类对象,用来设置对象前的内容
  • :before和::before写法是等效的; :after和::after写法是等效的

不同点

  • :before/:after是css2的写法,::before/::after是css3的写法
  • 所以css2的要比css3的兼容好 ,, :before/:after 的兼容性要比::before/::after好 ,
  • 不过在H5开发中建议使用::before/::after比较好

注意:

  1. 伪对象要配合content属性一起使用。
  2. 伪对象不会出现在DOM中,所以不能通过js来操作,仅仅是在 CSS 渲染层加入。
  3. 伪对象的特效通常要使用:hover伪类样式来激活。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
span{
background: yellow;
}
span:hover::before{
content: "我出现扩大了所受的煎熬";
}
</style>
</head>
<body>
<span>222还是666</span>
</body>
</html>
|