Vue 基础
Vue 简介
- JavaScript 框架
- 简化 Dom 操作
- 响应式数据驱动
第一个 Vue 程序
Vue 文档传送门:https://cn.vuejs.org
vue 2 文档等在页面下面可找到
el: 挂载点
<div id="app">
栋珞
</div>
var app = new Vue({
el: "#app",
data: {
message: "栋珞"
}
})
Vue 实例的作用范围是什么呢?
- Vue 会管理el选项命中的元素及其内部的后代元素
是否可以使用其他的选择器?
- 可以使用其他的选择器,但是建议使用ID选择器
是否可以设置其他的 dom 元素呢?
- 可以使用其他的双标签不能使用 HTML 和 BODY
el 是用来设置Vue实例挂载 (管理) 的元素
data: 数据对象
<div id="app">
{{message}}
</div>
var app = new Vue({
el: "#app",
data: {
message: '栋珞',
array: [],
obj: {}
}
})
- Vue 中用到的数据定义在 data 中
- data 中可以写复杂类型的数据
- 渲染复杂类型数据时,遵守 js 的语法即可
Vue 7 大属性
-
el属性
- 用来指示vue编译器从什么地方开始解析 vue的语法,可以说是一个占位符。
-
data属性
- 用来组织从view中抽象出来的属性,可以说将视图的数据抽象出来存放在data中。
-
template属性
- 用来设置模板,会替换页面元素,包括占位符。
-
methods属性
- 放置页面中的业务逻辑,js方法一般都放置在methods中
-
render属性
- 创建真正的Virtual Dom
-
computed属性
- 用来计算
-
watch属性
-
watch:function(new,old){}
-
监听data中数据的变化
-
两个参数,一个返回新值,一个返回旧值
-
什么是计算属性 computed
计算属性的重点突出在 属性 两个字上 (属性是名词),首先它是个 属性 其次这个属性有 计算 的能力 (计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性 (将行为转化成了静态的属性),仅此而已: 可以想象为缓存!
<div id="vue">
<!-- 注意,一个是方法,一个是属性 -->
<p>调用当前时间的方法:{{currentTime1()}}</p>
<p>当前时间的计算属性:{{currentTime2}}</p>
</div>
<script>
var app = new Vue({
el="#vue",
data: {
message: "hellow Vue"
},
methods:{
currentTime1: function() {
return Date.now()
}
},
computed: {
// currentTime2,这是一个属性!不是方法
currentTime2: function() {
this.message
return Date.now()
}
}
})
</script>
注意: methods 和 computed 里的东西不能重名,否则以 methods 来显示,优先级最高
说明:
- methods: 定义方法,调用方法使用 currentTime1(),需要带括号
- computed: 定义计算属性,调用属性使用 currentTime2,不需要带括号; this.message 是为了能够让 currentTime2 观察到数据变化而变化
- 如何在方法中的值发生了变化,则缓存就会刷新! 可以在控制台使用 vm.message=”donluo”,改变下数据的值,再次测试观察效果!
结论:
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销
本地应用
V-text
设置标签的文本值(textContent)
<div id="app">
<h2 v-text="message+'i'"></h2>
<h2>深圳{{message+'i'}}</h2>
</div>
var app = new Vue({
el:"#app",
data:{
message:"栋珞"
}
})
- v-text 指令的作用是:设置标签的内容(textContent)
- 默认写法会替换全部内容,使用==差值表达式{{}}==可以替换指定内容
- 内部支持写表达式
v-html
设置标签的innerHTML
<div id="app">
<p v-html="content"></p>
</div>
var app = new Vue({
el: "#app",
data: {
// content: "栋珞"
content: "<a href='#'>栋珞</a>"
}
})
- v-html指令的作用是:设置元素的innerHTML
- 内容中有html结构会被解析为标签
- v-text指令无论内容是什么只会解析为文本
- 解析文本使用v-text,需要解析html结构使用v-html
v-on基础
为元素绑定事件, 传递自定义参数事件修饰符
<div id="app">
<input type="button" value="事件绑定" v-on:click="doIt">
<input type="button" value="事件绑定" v-on:mounseenter="doIt">
<input type="button" value="事件绑定" v-on:dblclick="doIt">
<input type="button" value="事件绑定" @dblclick="doIt">
<input type="button" value="传参" @dblclick="doIt(p1,p2)">
<input type="text" value="按下回车调用sayHi方法" @keyup.enter="sayHi">
</div>
var app = new Vue({
el: "#app",
methods: {
doIt:function(p1,p2){
// 逻辑
},
sayHi: function(){
alert('你好哇')
}
}
})
- v-on指令的作用是: 为元素绑定事件
- 事件名不需要写on
- 指令可以简写为==@==
- 绑定的方法定义在methods属性中
- 方法内部通过this关键字可以访问定义在data中数据
- 事件绑定的方法写成函数调用的形式,可以传入自定义参数
- 定义方法时需要定义形参来接收传入的实参
- 事件的后面跟上 .修饰符 可以对事件进行限制
- .enter 可以限制触发的按键为回车
- 事件修饰符有多种
v-on参考文档:(https://cn.vuejs.org/)
计数器案例
<div class="input-num">
<buton @click="sub">-</buton>
<span v-text="num">{{num}}</span>
<buton @click="add">+</buton>
</div>
var app = new Vue({
el:".input-num",
data: {
num: 1
},
methods: {
add: function() {
if(this.num < 10) {
this.num++
} else {
alert("已到最大值")
}
},
sub: function() {
if(this.num > 0) {
this.num--
} else {
alert("已到最小值")
}
}
}
})
- data中定义数据: 比如num
- methods中添加两个方法:比如add(递增),sub(递减)
- 使用v-text将num设置给span标签
- 使用v-on将add,sub分别绑定给==+,-==按钮
- 累加的逻辑: 小于10累加,否则提示
- 递减的逻辑: 大于0递减否则提示
总结
- 创建Vue示例时: el(挂载点),data(数据),methods(方法
- v-on指令的作用是绑定事件,简写为==@==
- 方法中通过this, 关键字获取data中的数据
- v-text指令的作用是: 设置元素的文本值简写为=={{}}==
- v-html指令的作用是: 设置元素的innerHTML
v-show
根据表达值的真假,切换元素的显示和隐藏
<div id="app">
<img src="地址" v-show="true">
<img src="地址" v-show="isShow">
<img src="地址" v-show="age>=18">
</div>
var app = new Vue({
el: "#app",
data: {
isShow: false,
age:16
}
})
- v-show指令的作用是:根据真假切换元素的显示状态
- 原理是修改元素的display, 实现显示隐藏
- 指令后面的内容,最终都会解析为布尔值
- 值为true元素显示, 值为false元素隐藏
v-if,v-else-if,v-else
根据表达值的真假切换元素的显示和隐藏(操纵dom元素)
<div id="app">
<p v-if="true">我是一个p标签</p>
<p v-if="isShow">我是一个p标签</p>
<p v-else-if="isShow">我是一个p标签</p>
<p v-else="isShow">我是一个p标签</p>
<p v-if="表达式">我是一个p标签</p>
</div>
var app = new Vue({
el:"#app",
data:{
isShow:false
}
})
- v-if指令的作用是:根据表达式的真假切换元素的显示状态
- v-else-if和v-else和JavaScript语法类似使用方式
- 本质是通过操纵dom元素来切换显示状态
- 表达式的值为true, 元素存在于dom树中,为false, 从dom树中移除
- 频繁的切换v-show, 反之使用v-if, 前者的切换消耗小
v-bind
设置元素的属性(比如==:src==, title, class)
<div id="app">
<img :src="imgSrc">
<img :title="imgTitle+'!!!'">
<img :class="isActive?'active':''">
<img :class="{active:isActive}">
</div>
var app = new Vue({
el:'#app',
data:{
imgSrc:"图片地址",
imgTitle: "栋珞",
isActive: false
}
})
- v-bind指令的作用是:为元素绑定属性
- 完整写法是 v-bind:属性名
- 简写的话可以直接省略v-bind,只保留 :属性名
- 需要动态的增删class建议使用对象的方式
图片切换案例
- 定义图片数组
- 添加图片索引
- 绑定 src 属性
- 图片切换逻辑
- 显示状态切换
<div id="#app">
<img :src="imgArr[index]">
<a href="#" @click="prev" v-show="条件">上一张</a>
<a href="#" @click="next" v-show="条件">下一张</a>
</div>
var app = new Vue({
el:"#app",
data:{
imgArr:[],
index: 0
},
methods: {
prev:function(){},
next:function(){}
}
})
- 列表数据使用数组保存
- v-bind指令可以设置元素属性, 比如src
- v-show和v-if都可以切换元素的显示状态,频繁切换用v-show
v-for
根据数据生成列表结构
<div id="app">
<ul>
<li v-for="(item,index) in arr" :title="item">
{{item}}{{index}}
</li>
<li v-for="(item,index) in objArr">
{{item.name}}
</li>
</ul>
</div>
var app = new Vue({
el: "#app",
data: {
arr:[1,2,3,4,5],
objArr:[
{name:"jack"},
{name:"rose"}
]
}
})
- v-for指令的作用是:根据数据生成列表结构
- 数组经常和v-for结合使用
- 语法是==( item,index )in 数据==
- item 和 index 可以结合其他指令一起使用
- 数组长度的更新会同步到页面上,是响应式的
v-model
获取和设置表单元素的值(双向数据绑定)
<div id="app">
<input type="text" v-model="message">
</div>
var app = new Vue({
el:"#app",
data:{
message:"栋珞"
}
})
- v-model指令的作用是便捷的设置和获取表单元素的值
- 绑定的数据会和表单元素值相关联
- 绑定的数据←→表单元素的值
记事本案例
- 新增
- v-for指令的作用
- v-model指令的作用
- v-on指令,事件修饰符
- 通过审查元素快速定位
- 删除
- 数据改变和数据绑定的元素同步改变
- 事件的自定义参数
- splice方法的作用
- 统计
- 基于数据的开发方式
- v-text指令的作用
- 清空
- 基于数据的开发方式
- 隐藏
- 没有数据时隐藏元素(v-show v-if 数组非空)
总结
- 列表结构可以通过v-for指令结合数据生成
- v-on结合事件修饰符可以对事件进行限制,比如 .enter
- v-on在绑定事件时可以传递自定义参数
- 通过v-model可以快速的设置和获取表单元素的值
- 基于数据的开发方式
组件 component
使用 Vue.componen () 方法注册组件,格式如下
<div id="app">
<donluo v-for="item in items" v-bind:luo="item"></donluo>
</div>
<script>
Vue.component("donluo", {
props:["luo"],
template: '<a>{{luo}}</a>'
});
const app = new Vue({
el:"#app",
data:{
items:["a","b","c"]
}
})
</script>
- Vue.component(): 注册组件
- donluo: 自定义组件的名字
- template: 组件的模板
- 使用 props 属性传递参数
注意: 默认规则下 props 属性里的值不能为大写!!!;
- v_for = "item in items : 量遍历 Vue 实例中定义的名为 items 的数组,并创建同等数的组件
- v-bind:luo=“item” : 将遍历的 item 项绑定到组件中 props 定义的名为 luo 属性上; = 号左边的 luo 为 props 定义的属性名,右边的为 item in items 中遍历的 item 项的值
插槽 slot
在 Vue.js 中我们使用 < slot> 元素作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中
比如准备制作一个待办事项组件 (todo),该组件由待办标题 (todo-title) 和待办内容 (todo-items)组成,但这三个组件又是相互独立的,该如何操作呢?
第一步:定义一个代办事项的组件
<div id="vue">
<todo></todo>
</div>
<script>
Vue.component('todo', {
template: '<div>\
<div>待办事项</div>\
<ul>\
<li>栋珞</li>\
</ul>\
</div>'
})
</script>
第二步: 我们需要让,待办事项的标题和值实现动态绑定,怎么做呢? 我们可以留出一个插槽!
-
将上面的代码留出一个插槽,即 slot
Vue.component('todo', { template: '<div>\ <div>待办事项</div>\ <ul>\ <li>栋珞</li>\ </ul>\ </div>' })
-
定义一个名为 todo-title 的待办标题组件和 todo-items 的待办内容组件
Vue.component('todo-title', { props: ['title'], template: '<div>{{title}}</div>' })
Vue.component('todo-items',{ props:['item', 'index'], template:'<li>{{index + 1}}. {{item}}</li>' })
-
实例化 Vue 并初始化数据
var app = new Vue({ el: "#vue", data: { todoTitle: 'Vue标题', todoItems: ['a','b','c'] } })
-
将这些值,通过插槽插入
<div> <todo> <todo-title slot="todo-title" v-bind:title="todoTitle"></todo> <todo-items slot="todo-items" v-for="(item, index) in todoItems" :item="item", v-bind:index="index"></todo-items> </todo> </div>
说明: 我们的 todo-title 和 todo-items 组件分别被分发到了 todo 组件的 todo-title 和 todo-items 插槽中
自定义事件
通过以上代码不难发现,数据项在 Vue 的实例中,但删除操作要在组件中完成,那么组件如何才能删除 Vue 实例中的数据呢? 此时就涉及到参数传递与事件分发了,Vue 为我们提供了自定义事件为功能很好的帮助我们解决了这个问题,使用 this.semit(自定义事件名,参数),操作过程如下:
-
在vue的实例中增加了 methods 对象并定义了一个名为 removeTodoltems 的方法
var vm = new Vue({ el: "#vue", data: { title: "Hello Vue!", todoItems: ['a', 'b', 'c'] }, methods:{ // 该方法可以被模版中自定义事件触发 removeTodoItems: function(index) { console.log("删除" + this.todoItems[index] + "成功"); // splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目,其中 index 为开始删除的第一个元素 this.todoItems.splice(index, 1); } } })
-
修改 todoitems 待办内容组件的代码,增加一个删除按钮,并且绑定事件!
Vue.component('todotitle', { props: ['item', 'index'], template: '<li>{{index}}--{{item}}<button @click="re">删除</button></li>', methods: { re: function(index) { // 这里的 remove 是自定义事件的名称,需要在 HTML 中使用 v-on:re 的方式绑定自定义事件 this.$emit('re', index) } } })
-
修改 todoitems 待办内容组件的 HTML 代码,增加一个自定义事件,比如叫 remove,可以和组件的方法绑定,然后绑定到vue的方法中!
<!--增加了 v-on:re="removeTodoItems(index)" 自定义事件,该事件会调用 Vue 实例中定义的 removeTodoItems 方法--> <todoitems slot="todo-items" v-for="(item, index) in todoItems" v-bind:item="item" v-bind:index="index" :key="index" v-on:re="removeTodoItems(index)"></todoitems>
Vue 入门小结
核心: 数据驱动,组件化
优点: 借鉴了 AngulaJS 的模块化开发 和 React 的虚拟Dom,虚拟Dom就是把Dom操作放到内存中执行;
常用的属性:
- v-if
- V-else-if
- V-else
- V-for
- v-on 绑定事件,简写 @
- v-model 数据双向绑定
- v-bind 给组件绑定参数,简写 :
组件化:
- 组合组件 slot 插槽
- 组件内部绑定事件需要使用到 this.$emit("事件名”,参数);
- 计算属性的特色,缓存计算数据
遵循 SoC 关注度分离原则,Vue是纯粹的视图框架,并不包含,比如Aiax之类的通信功能,为了解决通信问题,我们需要使用 Axios 框架做异步通信;
网络应用
Vue结合网络数据开发应用
axlos
什么是Axios
Axios 是一个开源的可以用在浏览器端和 NodeJs 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API[JS中链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF (跨站请求伪造)
功能强大的网络请求库
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios.get(地址?key=value&key2=values).then(function(response){},function(err){})
axios.post(地址,{key=value&key2=values}).then(function(response){},function(err){})
- axios必须先导入才可以使用
- 使用get或post方法即可发送对应的请求
- then方法中的回调函数会在请求成功或失败时触发
- 通过回调函数的形参可以获取响应内容,或错误信息
文档传送门
GitHub: https://github.com/axios/axios
中文文档: http://www.axios-js.com/
axios + Vue
<div id="app">
<button @click="getXiaoHua">获取笑话</button>
<div>{{xiaoHua}}</div>
</div>
// 接口1: 随机笑话
// 请求地址:https://autumnfish.cn/api/joke/list
// 请求方法:get请求参数:num(笑话条数,数字)
// 响应内容:随机笑话
var app = new Vue({
el: "#app",
data: {
xiaoHua: ''
},
methods: {
getXiaoHua: function() {
const then = this
axios.get("https://autumnfish.cn/api/joke/list?num=1").then(function(response) {
then.xiaoHua = response.data.data[0]
}, function(err) {
console.log(err)
})
}
}
})
- axios回调函数中的this已经改变无法访问到data中数据
- 把this保存起来回调函数中直接使用保存的this即可
- 和本地应用的最大区别就是改变了数据来源
天知道案例
查询天气的应用
回车查询
- 按下回车(v-on .enter)
- 查询数据(axios 接口 v-model)
- 染数据(v-for 数组 that)
- 应用的逻辑代码建议和页面分离,使用单独的 js 文件编写
- axios回调函数中this指向改变了,需要额外的保存一份
- 服务器返回的数据比较复杂时,获取的时候需要注意层级结构
点击查询
- 点击城市(v-on 自定义参数)
- 查询数据(this.方法())
- 渲染数据
- 自定义参数可以让代码的复用性更高
- methods中定义的方法内部,可以通过this关键字点出其他的方法
Vue-cli
什么是vue-cli
vue-cli 官方提供的一个脚手架,用于快速生成一个 vue 的项目模板
预先定义好的目录结构及基础代码,就好比咱们在创建 Maven 项目时可以选择创建一个骨架项这个骨架项目就是脚手架,我们的开发更加的快速;
主要的功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要的环境
-
Node.js : http://nodejs.cn/download/
安装就无脑下一步就好,安装在自己的环境目录下
-
Git : https://git-scm.com/downloads
镜像:https://npm.taobao.org/mirrors/git-for-windows/
确认nodejs安装成功:
- cmd 下输入 node -v,查看是否能够正确打印出版本号即可
- cmd 下输入 npm -v ,查看是否能够正确打印出版本号即可!
这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多!
安装 Node.js 淘宝镜像加速器 (cnpm)
这样子的话,下载会快很多~
# -g 就是全局安装
npm install cnpm -g
# 或使用如下语句解决 npm 速度慢的问题
npm install cnpm -g --registry=https://registry.npm.taobao.org # 淘宝镜像地址更新了,可以去百度看看
安装过程可能有点慢~,耐心等待!虽然安装了cnpm,但是尽量少用!
安装的位置: c:\Users\Administrator\AppData\Roaming\npm,不一定,看自己一般都是这个路径除了Administrator用户名
安装 vue-cli
cnpm install vue-cli -g
# 测试是否安装成功
# 查看可以基于哪些模板创建 vue 应用程序,通常我们选择 webpack
vue list
第一个 vue-cli 应用程序
-
创建一个Vue项目
-
创建一个基于 webpack 模版的 Vue 应用程序
# 这里的 myvue 是项目名称,可以根据自己的需求起名 vue init webpack myvue
一路都选择 no 即可
说明:
- Project name: 项目名称,默认 回车 即可
- Project description: 项目描述,默认 回车 即可
- Author: 项目作者,默认 回车 即可
- lnstall vue-router: 是否安装 vue-router,选择 n 不安装 (后期需要再手动添加)
- Use ESLint to lint your code: 是否使用 ESLint 做代码检查,选择 n 不安装 (后期需要再手动添加)
- Set up unit tests: 单元测试相关,选择 n 不安装(后期需要再手动添加)
- Setup e2e tests with Nightwatch: 单元测试相关,选择 n 不安装 (后期需要再手动添加)
- Should we run npm install for you after the project has been created: 创建完成后直接初始化,选择 n,我们手动执行;运行结果!
初始化并运行
cd myvue # 去到刚才创建的项目路径下
npm install
npm run dev
执行完成后,目录多了很多依赖
Webpack
什么是Webpack
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
Webpack 是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过 loader 转换,任何形式的资源都可以当做模块,比如 CommonsJS.AMD、ES6、CSS、JSON、CoffeeScript、LESS 等
伴随着移动互联网的大潮,当今越来越多的网站已经从网页模式进化到了 WebApp 模式。它们运行在现代浏览器里,使用 HTML5、CSS3、ES6 等新的技术来开发丰富的功能,网页已经不仅仅是完成浏览器的基本需求;WebApp 通常是一个 SPA (单页面应用),每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS 代码,这给前端的开发流程和资源组织带来了巨大挑战
前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题
模块化的演进
script 标签
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="module3.js"></script>
<script src="module4.js"></script>
这是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的调用都是一个作用域
这种原始的加载方式暴露了一些显而易见的弊端:
- 全局作用域下容易造成变量冲突
- 文件只能按照 < script> 的书写顺序进行加载
- 开发人员必须主观解决模块和代码库的依赖关系
- 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
CommonsJS
服务器端的 NodeJS 遵循 CommonsJS 规范,该规范核心思想是允许模块通过 require 方法来同步加载所需依赖的其它模块,然后通过 exports 或 module.exports 来导出需要暴露的接口
require("module");
require("../module.js" );
export.doStuff = function() {};
module.exports = someValue;
优点:
- 服务器端模块便于重用
- NPM 中已经有超过 45 万个可以使用的模块包
- 简单易用
缺点:
- 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
- 不能非阻塞的并行加载多个模块
实现:
- 服务端的 NodeJS
- Browserify,浏览器端的 CommonsJS 实现,可以使用 NPM 的模块,但是编译打包后的文件体积较大
- modules-webmake,类似 Browserify,但不如 Browserify 灵活
- wreq,Browserify 的前身
AMD
Asynchronous Module Definition 规范其实主要一个主要接口 define(id?dependencies?,factory); 它要在声明模块的时候指定所有的依赖 dependencies,并且还要当做形参传到 factory 中,对于依赖的模块提前执行
define("module", ["dep1","dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module","../file.js"], function(module, file) {});
优点:
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
缺点:
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
- 不符合通用的模块化思维方式,是一种妥协的实现
实现
- RequireJS
- curl
CMD
Commons Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonsJS 和 NodeJS 的 Modules 规范保持了很大的兼容性
define(function(require, exports, module) {
var $ = require("jquery");
var Spinning = require("./spinning");
exports .doSomething = ...;
module.exports = ...;
});
优点:
- 依赖就近,延迟执行
- 可以很容易在 NodeJS 中运行
缺点
- 依赖 SPM 打包,模块的加载逻辑偏重
实现
- Sea.js
- coolie
ES6 模块
kcmaScript6 标准增加了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS 和 AMD 模块,都只能在运行时确定这些东西。
import "jquery";
export function doStuff() {}
module "localModule" {}
优点
- 容易进行静态分析
- 面向未来的 EcmaScript 标准
缺点
- 原生浏览器端还没有实现该标准
- 全新的命令,新版的 NodeJS 才支持
实现
- Babel
安装 Webpack
WebPack 是一款模块加载器兼打包工具,它能把各种资源,如 JS、JSX、ES6、SASS、LESS.
图片等都作为模块来处理和使用
安装:
npm install webpack -g
npm install webpack-cli -g
测试安装成功:
- webpack -v
- webpack-cli -v
配置
创建 webpack.config.js 配置文件
- entry: 入口文件,指定 WebPack 用哪个文件作为项目的入口
- output: 输出,指定 WebPack 把处理完成的文件放置到指定路径。module: 模块,用于处理各种类型的文件
- plugins: 插件,如: 热更新、代码重用等
- resolve: 设置路径指向
- watch: 监听,用于设置文件改动后直接打包
module.exports = {
entry: "",
output: {
path:"",
filename: ""
},
module: {
loaders: [
{test: /\.js$/, loader: ""}
]
},
plugins: {},
resolve: {},
watch: true
}
直接运行 webpack 命令打包
使用webpack
-
创建项目
-
创建一个名为 modules 的目录,用于放置 JS 模块等资源文件
-
在 modules 下创建模块文件,如 hello.js,用于编写 JS 模块相关代码
// 暴露一个方法: sayHi exports.sayHi = function() { document.write("<div>Hello Webpack</div>") };
-
在 modules 下创建一个名为 main.js 的入口文件,用于打包时设置 entry 属性
// require 导入一个模块,就可以调用这个模块中的方法了 var hello = require("./hello"); hello.sayHi();
-
在项目目录下创建 webpack.config.js 配置文件,使用 webpack 命令打包
module.exports = { entry: "./modules/main.js", output: { filename: "./js/bundle.js" } }
-
在项目目录下创建 HTML 页面,如 irdex.html,导入 WebPack 打包后的 JS 文件
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script src="dist/js/bundle.js"></script> </body> </html>
-
在IDEA控制台中直接执行webpack;如果失败的话,就使用管理员权限运行即可!
-
运行 HTML 看效果
说明:
# 参数 --watch 用于监听变化
webpack --watch
vue - router 路由
说明
学习的时候,尽量的打开官方的文档
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vuejs 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
安装
基于第一个 vue-cli 进行测试学习;先查看node_modules中是否存在 vue-router
vue-router 是一个插件包,所以我们还是需要用 npm/cnpm 来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) ;
测试
-
先删除没有用的东西
-
components 目录下存放我们自己编写的组件
-
定义一个 Content.vue 的组件
<template> <div> <h1>内容页</h1> </div> </template> <script> export default { name: "Content" } </script>
-
安装路由,在src目录下,新建一个文件夹 : router,专门存放路由
import Vue from 'vue' // 导入路由插件 import Router from 'vue-router' // 导入上面定义的组件 import Content from'../components/Content' import main from '../components/main' // 安装路由 Vue.use(Router); // 配置路由 export default new Router({ reouters: [ { // 路由路径 path: '/content', name: 'Content', // 跳转到组件 component: Content }, { // 路由路径 path: '/main', name: 'main', // 跳转到路由 component: main } ] })
-
在 main.js 中配置路由
import Vue from 'vue' import App from './App' // 导入上面创建的路由配置目录 import router from './router' // 来关闭生产模式下给出的提示 Vue.config.productionTip = false new Vue({ el: '#app', // 配置路由 router, components: {App} template: '<App/>' })
-
在 App.vue 中使用路由
<template> <div id="app"> <!-- router-link: 默认会被渲染成一个 <a> 标签,to 属性为指定链接 router-view: 用于渲染路由匹配到的组件 --> <router-link to="/">首页</router-link> <router-link to="/content">内容</router-link> <router-view>< /router-view> </div> </template> <script> export default { name :'App' } </script> <style> </style>
Element 库实战
创建工程
注意: 命令行都要使用管理员模式运行
-
创建一个名为 hello-vue 的工程 vue init webpack hello-vue
-
安装依赖,我们需要安装 vue-router、element-ui、sass-loader 和 node-sass 四个插件
# 进入工程目录 cd hello-vue # 安装 vue-router npm install vue-router --save-dev # 安装 element-ui npm install element-ui -s # 安装 SASS 加载器 npm install sass-loader node-sass --save-dev # 启动测试 npm run dev
-
npm 命令解释:
- npm install moduleName: 安装模块到项目目录下
- npm install -g moduleName:-g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm config prefix 的位置
- npm install --save moduleName: --save 的意思是将模块安装到项目目录下,并在package 文件的 dependencies 节点写入依赖,-S 为该命令的缩写
- npm install --save-dev moduleName:–save-dev 的意思是将模块安装到项目目录下,并在 package 文件的 devDependencies 节点写入依赖,-D 为该命令的缩写
-
查看 element 文档导入组件
<template> <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="密码" prop="pass"> <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input> </el-form-item> <el-form-item label="确认密码" prop="checkPass"> <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input> </el-form-item> <el-form-item label="年龄" prop="age"> <el-input v-model.number="ruleForm.age"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button> <el-button @click="resetForm('ruleForm')">重置</el-button> </el-form-item> </el-form> </template> <script> export default { data() { var checkAge = (rule, value, callback) => { if (!value) { return callback(new Error('年龄不能为空')); } setTimeout(() => { if (!Number.isInteger(value)) { callback(new Error('请输入数字值')); } else { if (value < 18) { callback(new Error('必须年满18岁')); } else { callback(); } } }, 1000); }; var validatePass = (rule, value, callback) => { if (value === '') { callback(new Error('请输入密码')); } else { if (this.ruleForm.checkPass !== '') { this.$refs.ruleForm.validateField('checkPass'); } callback(); } }; var validatePass2 = (rule, value, callback) => { if (value === '') { callback(new Error('请再次输入密码')); } else if (value !== this.ruleForm.pass) { callback(new Error('两次输入密码不一致!')); } else { callback(); } }; return { ruleForm: { pass: '', checkPass: '', age: '' }, rules: { pass: [ { validator: validatePass, trigger: 'blur' } ], checkPass: [ { validator: validatePass2, trigger: 'blur' } ], age: [ { validator: checkAge, trigger: 'blur' } ] } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } </script>
import Vue from 'vue' import App from './App' import Router from './router' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(Router) Vue.use(ElementUI) /* eslint-disable no-new */ new Vue({ el: '#app', Router, render: h => h(App) // ElementUI })
-
配置 router 跳转
import Vue from 'vue' import Router from 'vue-router' import Main from '../views/Main' import Login from '../views/Login' Vue.use(Router) export default new Router({ router: [{ path: '/Main', component: Main }, { path: '/Login', component: Login }] })
测试:在浏览器打开 http://localhost:8080/#/login
如果出现错误:可能是因为sass-loader的版本过高导致的编译错误,当前最高版本是8.x,需要退回到7.3.1
去package.json文件里面的 “sass-loader” 的版本更换成7.3.1,然后重新 cnpm install 就可以了:
路由嵌套
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
-
用户信息组件,在 views/user 目录下创建一个名为 Profile.vue 的视图组件
<template> <div> 个人信息 </div> </template> <script> export default { name: "UserProfile" } </script> <style scoped> </style>
-
用户列表组件在 views/user 目录下创建一个名为 List.vue 的视图组件;
<template> <div>用户列表</div> </template> <script> export default { name: "UserList" } </script> <style scoped> </style>
-
配置嵌套路由修改 router 目录下的 index.js 路由配置文件,代码如
import Vue from 'vue' import Router from 'vue-router' import Login from "../views/Login" import Main from "../views /Main" // 用于嵌套的路由组件 import UserProfile from "../views/user/Profile" import UserList from "../views/user/List" Vue.use(Router); export default new Router({ routes: [{ // 登录页 path:'/login' name: 'Login' component: Login },{ // 首页 path: '/main', name : 'Main', component: Main, // 配置嵌套路由 children: [ {path :'/user/profile', component: UserProfile}, {path :'/user/list', component: UserList}] }] })
说明: 主要在路由配置中增加了 children 数组配置,用于在该组件下设置嵌套路由
-
修改首页视图,我们修改 Main.vue 视图组件,此处使用了 ElementUl 布局容器组件
组件重定向
重定向的意思大家都明白,但 Vue 中的重定向是作用在路径不同但组件相同的情况下,比如:
{
path: '/main',
name: 'Main',
component: Main
},{
path: '/goHome',
redirect: '/main'
}
说明: 这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件;
使用的话,只需要设置对应路径即可
<el-menu-item index="1-3">
<router-link to="/goHome">回到首页</router-link>
</el-menu-item>
路由模式与 404
路由模式有两种
- hash: 路径带 # 符号,如 http://localhost/#/login
- history: 路径不带 # 符号,如 http://localhost/login
修改路由配置,代码如下:
export default new Router({
fmode: 'history',
routes: [
]
})
处理 404 创建一个名为 NotFound.vue 的视图组件,代码如下
<template>
<div>
页面不存在,请重试!
</div>
</template>
<script>
</script>
import NotFound from '../views/NotFound'
{
path: "*",
component: NotFound
}
路由钩子与异步请求
beforeRouteEnter:在进入路由前执行
beforeRouteLeave:在离开路由前执行
export default {
props: ['id'],
name: "UserProfile",
beforeRouterEnter: (to, from, next) => {
console.log("准备进入个人信息页")
next()
},
beforeRouteLave: (to, from, next) => {
console.log("准备离开个人信息页")
next()
}
}
参数说明:
- to: 路由将要跳转的路径信息
- from: 路径跳转前的路径信息
- next: 路由的控制参数
- next() 跳入下一个页面
- next(‘/path’) 改变路由的跳转方向,使其跳到另一个路由
- next(false) 返回原来的页面
- next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
在钩子函数中使用异步请求
-
安装 Axios cnpm install axios -s
-
main.js 引用 Axios
import axios from 'axios' Vue.prototype.axios = axios
-
准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放
入该目录下// 静态存放的位置 static/mock/data.json
-
在 beforeRouteEnter 中进行异步请求
beforeRouteEnter: (to, from, next) => { console.log("进入路由之前"); next(vm => { vm.getData(); // 进入路由之前执行getData方法 }) }
Vuex
Vuex概述
index=“1-3”>
回到首页
# 路由模式与 404
路由模式有两种
- hash: 路径带 # 符号,如 http://localhost/#/login
- history: 路径不带 # 符号,如 http://localhost/login
修改路由配置,代码如下:
```JavaScript
export default new Router({
fmode: 'history',
routes: [
]
})
处理 404 创建一个名为 NotFound.vue 的视图组件,代码如下
<template>
<div>
页面不存在,请重试!
</div>
</template>
<script>
</script>
import NotFound from '../views/NotFound'
{
path: "*",
component: NotFound
}
路由钩子与异步请求
beforeRouteEnter:在进入路由前执行
beforeRouteLeave:在离开路由前执行
export default {
props: ['id'],
name: "UserProfile",
beforeRouterEnter: (to, from, next) => {
console.log("准备进入个人信息页")
next()
},
beforeRouteLave: (to, from, next) => {
console.log("准备离开个人信息页")
next()
}
}
参数说明:
- to: 路由将要跳转的路径信息
- from: 路径跳转前的路径信息
- next: 路由的控制参数
- next() 跳入下一个页面
- next(‘/path’) 改变路由的跳转方向,使其跳到另一个路由
- next(false) 返回原来的页面
- next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
在钩子函数中使用异步请求
-
安装 Axios cnpm install axios -s
-
main.js 引用 Axios
import axios from 'axios' Vue.prototype.axios = axios
-
准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放
入该目录下// 静态存放的位置 static/mock/data.json
-
在 beforeRouteEnter 中进行异步请求
beforeRouteEnter: (to, from, next) => { console.log("进入路由之前"); next(vm => { vm.getData(); // 进入路由之前执行getData方法 }) }