2014年诞生,2013:react,2009:angularjs
渐进式的js框架:也就是没有太多的限制,易用,灵活,高效
而angularjs:只能用自己的组件,排他性(限制)较强
核心概念:组件化(双向数据流,基于ES5中的defineProperty实现,监视当前声明的属性值改变,让页面发生改变)IE9才支持(有兼容性问题)
angularjs核心:模块化(双向数据绑定,基于脏检测:就是一个数组($watch)每次修改都要检测每个$watch)
组件化和模块化就是细分代码的,在代码层分为template,style,script
组件化分的会更细。
双向数据流:
1.js内存属性发生改变,影响页面的变化
2.页面的改变影响js内存属性的变化
常用指令:
v-text
v-html
v-if
v-show
v-model
方法绑定一定要注意阻止默认事件
//1.绑定后+.prevent
<form @submit.prevent="addCustomer">
//2.在定义的方法末尾:
addCustomer:function(e){
.......
e.preventDefault();
}
Vue CLI脚手架
- 是通过webpack搭建的开发环境
- 使用ES6语法而不必担心浏览器是否支持
- 打包和压缩js为一个文件
- 项目文件在环境中编译,而非浏览器
- 实现页面自动刷新
命令行工具安装全局cli:
确保安装好node.js后,cmd键入:
npm install --global vue-cli
或使用cnpm installl -g vue-cli
cmd键入:
vue --version
然后配置一个项目:
选择一个文件夹,vue init webpack 项目名
或使用 vue init webpack-simple pizza-app
(简版)
cmd进入刚才定义的项目文件夹,键入npm install
键入:npm run dev
启动服务。
使用路由功能需安装:npm install vue-router --save-dev
使用vue-resource进行http请求需安装:npm install vue-resource --save-dev
使用vuex进行数据管理需要:cnpm install vuex --save
使用国内镜像:
1、临时使用
npm --registry https://registry.npm.taobao.org install express
2、持久使用
npm config set registry https://registry.npm.taobao.org
// 配置后可通过下面方式来验证是否成功
npm config get registry
// 或
npm info express
3、通过cnpm使用
npm install -g cnpm --registry=https://registry.npm.taobao.org
// 使用
cnpm install express
组件嵌套大致步骤:
先在父组件中import,然后在<script>中加入components,进行注册,最后在<template>中将刚才进行注册的名称作为标签写入。
父组件向子组件传值大致步骤
上一步将子组件的注册名作为标签引入到主组件,那么在该标签里用v-bind:自定名=“data中定义的属性名”,传值成功,子组件中在<script>中加入props:{自定名:{type:String}},实现父组件向子组件传值。
子组件向父组件传值大致步骤(通过事件传值)
定义一个事件,在methods中的function中加入this.$emit(“key”,“value”);传值成功,在父组件里的将嵌入到主组件的标签中加入:v-on:titleChanged=“update($evnet)” ,然后在mathods中实现该方法。
父组件:
<!--模板 有且只能有一个根标签-->
<template>
<div id="app">
<app-header v-on:titleChanged="update($evnet)" v-bind:title="title"></app-header>
<users v-bind:users="users"></users><!--向子对象传值 2 -->
<alert v-bind:message="alert"></alert><!--向子对象传值 1 -->
<users v-bind:users="users"></users>
<app-footer v-bind:tit="title"></app-footer>
</div>
</template>
<!--行为(处理逻辑) -->
<script>
/* 局部注册组件 */
import Users from './components/Users'
import Header from './components/Header'
import Footer from './components/Footer'
import Alert from './Alert'
export default {
name: 'App',
data:function(){
return{
//title:"这是我的第一个Vue脚手架"
users:[
{name:"jack",position:"web开发",show:false},
{name:"tom",position:"web开发",show:false},
{name:"jims",position:"web开发",show:false},
{name:"henry",position:"web开发",show:false},
],
title:"传递的是一个值(String,number,Boolean)",
alert:"父向子传值"
}
},
methods:{
update(t){
this.title=t;
}
},
components:{
"users":Users,
"app-header":Header,
"app-footer":Footer,
"alert":Alert
}
}
</script>
<style>
h1{
color: purple;
}
</style>
子组件:
<template>
<header>
<h1>
<strong>{{message}}</strong>//接收父组件传的值
{{title1}},{{title}}
</h1>
<button @click="changeTitle">改制</button>
</header>
</template>
<script>
export default {
name:'app-header',
props:["message"],//接收1
props:{ //接收2
title:{
type:String
}
},
data() {
return {
title1:"Vue.js Demo"
};
},
methods:{
changeTitle:function(){
/* this.title="changed"; */
this.$emit("titleChanged","子向父传值");
}
}
}
</script>
<style scoped>
header{
background: lightgreen;
padding: 10px;
}
h1{
color: #222;
text-align: center;
}
</style>
生命周期-钩子函数:
beforeCreate:function(){
alert("组件实例化之前执行的函数");
},
created:function(){
alert("组件实例化完毕,页面未显示(数据初始化,DOM未生成)");
},
beforeMount:function(){
alert("组件挂在前,页面仍未展示,但虚拟dom已经配置");
},
mounted:function(){
alert("组件挂载后(数据装载到DOM),方法执行,页面显示(DOM已生成)");
},
beforeUpdate:function(){
alert("组件更新前,页面仍未更新。但虚拟dom已经配置");
},
updated:function(){
alert("组件更新,此方法执行后,页面显示");
},
beforeDestroy:function(){
alert("组件销毁前");
},
destroyed:function(){
alert("组件销毁后");
}
传值和传引用:
传值:String ,number,Boolean
传引用:array,object,那么修改和这个引用相关的值,所有的都会改变
使用路由功能需安装:npm install vue-router --save-dev
使用路由功能的大致步骤:
安装成功后,在main.js中:import VueRouter from ‘vue-router’,然后Vue.use(VueRouter),在实例化Vue中加入:router,然后//配置路由: const router=new VueRouter({ routes:[ {path:"/",component:Home}, {path:"/helloworld",component:HelloWorld} ], mode:"history" })
,然后再全局组件中的template里<router-view></router-view><!-- 把路由到的组件显示 -->
对比<a>标签跳转页面一定重新加载,router-link实现无刷新跳转
<li><a href="/">a-home</a></li><!-- 使用a标签每次都会页面重新加载 -->
<li><router-link to="/helloworld">router-hello</router-link></li>
//可以用变量当路径,:to ,可以改变其默认的<a\>为<div\>
<li><router-link :to="homeLink" tag="div" class="nav-link">主页</router-link></li>
//main.js中:{path:'/menu',name:'menulink',component:Menu},
<li><router-link :to="{name:'menulink'}" class="nav-link">菜单</router-link></li>
使用vue-resource进行http请求需安装:npm install vue-resource --save-dev
使用vue-resource功能的大致步骤:
安装成功之后,在main.js中import VueResource from ‘vue-resource’,然后Vue.use(VueResource),这样就可以在所有的组件中使用,使用方法:使用钩子函数created(){ this.$http.get("http://jsonplaceholder.typicode.com/users") .then((data)=>{ this.users=data.body; }) }
页面加载之前就请求数据
main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import App from './App'
import HelloWorld from './components/HelloWorld'
import Home from './components/Home'
Vue.config.productionTip = false
Vue.use(VueRouter)
Vue.use(VueResource)
//配置路由:
const router=new VueRouter({
routes:[
{path:"/",component:Home},
{path:"/helloworld",component:HelloWorld},
{path:'*',redirect:'/'}//以上没有匹配到就重定向到 /
],
mode:"history"
})
new Vue({
router,
el: '#app',
components: { App },
template: '<App/>'
})
if (module.hot) {
module.hot.accept();
}
//index.html->main.js->App.Vue
全局组件app.vue:
<!--模板 有且只能有一个根标签-->
<template>
<div id="app">
<app-header v-on:titleChanged="update($evnet)" v-bind:title="title"></app-header>
<users v-bind:users="users"></users><!--向子对象传值 -->
<users v-bind:users="users"></users>
<app-footer v-bind:tit="title"></app-footer>
<router-view></router-view>//路由到谁谁显示,不必在这里进行引入操作
</div>
</template>
<!--行为(处理逻辑) -->
/* 局部注册组件 */
import Users from './Users'
import Header from './Header'
import Footer from './Footer'
export default {
name: 'App',
data:function(){
return{
//title:"这是我的第一个Vue脚手架"
users:[
/* {name:"jack",position:"web开发",show:false},
{name:"tom",position:"web开发",show:false},
{name:"jims",position:"web开发",show:false},
{name:"henry",position:"web开发",show:false}, */
],
title:"传递的是一个值(String,number,Boolean)"
}
},
methods:{
update(t){
this.title=t;
}
},
components:{
"users":Users,
"app-header":Header,
"app-footer":Footer
},
created(){
this.$http.get("http://jsonplaceholder.typicode.com/users")
.then((data)=>{
this.users=data.body;
})
}
}
</script>
路由跳转方式:
methods:{
gotoMenue:function(){
跳转上次浏览的页面
this.$router.go(-1);
指定跳转的地址
this.$router.replace('/menu');
指定跳转路由的名字
this.$router.replace({name:'menulink'});
压栈跳转(常用)
this.$router.push('/menu');
this.$router.push({name:'menulink'});
}
}
二级路由及多级路由:
大致思路:先在main.js引入,然后在要开启多级路由的父路由上写children属性,且在父级路由上写redirect,默认指向那个路由
main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import Home from './components/Home'
import Menu from './components/Menu'
import Admin from './components/Admin'
import About from './components/about/About'
import Login from './components/Login'
import Regisiter from './components/Regisiter'
//二级路由
import Contact from './components/about/Contact'
import Deilivery from './components/about/Deilivery'
import History from './components/about/History'
import OrderingGuide from './components/about/OrderingGuide'
//三级路由
import Phone from './components/about/contact/Phone'
import PersonName from './components/about/contact/PersonName'
Vue.use(VueRouter)
const routes=[
{path:'/',component:Home},
{path:'/menu',name:'menulink',component:Menu},
{path:'/admin',component:Admin},
{path:'/about',name:'aboutLink',redirect:'/about/deilivery',component:About,children:[
{path:'/about/contact',name:'contactLink',redirect:'/about/contact/phone',component:Contact,children:[
{path:"/about/contact/phone",name:"phoneNum",component:Phone},
{path:"/about/contact/pname",name:"personName",component:PersonName}
]},
{path:'/about/deilivery',name:'deiliveryLink',component:Deilivery},
{path:'/about/history',name:'historyLink',component:History},
{path:'/about/orderingGuide',name:'orderingGuideLink',component:OrderingGuide}
]},
{path:'/login',component:Login},
{path:'/regisiter',component:Regisiter},
{path:'*',redirect:'/'}//以上没有匹配到就重定向到 /
]
const router=new VueRouter({
routes,
mode:'history'
})
new Vue({
el: '#app',
router,
render: h => h(App)
})
全局守卫
在main.js中:
//全局守卫
router.beforeEach((to,from,next)=>{
//console.log(to);
//判断store.getters.isLogin===false
if(to.path=='/login' || to.path=='/regisiter'){
next();
}else{
alert("请先行登录");
next('/login');
}
})
//后置钩子 (基本用不到)
router.afterEach((to,from)=>{
alert("after Each");
})
路由独享守卫
全局守卫对所有的路由
{path:'/admin',component:Admin,beforeEnter:(to,from,next)=>{
alert("非登录状态,不能访问");
next('/login');
}},
组件守卫
<template>
<h1>Admin</h1>
</template>
<script>
export default {
data(){
return{
name:'henry'
}
},
beforeRouteEnter:(to,from,next)=>{
alert("hello " +this.name);//拿不到name
next() ;
next(vm=>{ //使用异步回调则可以
alert("Hello"+vm.name);
})
},
beforeRouterLeave(to,from,next){
if((confirm("确定离开嘛?")) == true){
next()
}else{
next(false)
}
}
}
</script>
router抽离:
将main.js中的const routes和与之对应的import剪切到新建js中,并在const 前加上export,以便让main.js引入。
新建js:
main.js:
<router-view>复用:
先在要用到的.vue文件中指定位置加上name属性:
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-4">
<router-view name="orderingGuide"></router-view>
</div>
<div class="col-sm-12 col-md-4">
<router-view name="delivery"></router-view>
</div>
<div class="col-sm-12 col-md-4">
<router-view name="history"></router-view>
</div>
</div>
</div>
然后将router.js中对应的全局组件的component改为components:
{path:'/',components:{
default:Home,
"orderingGuide":OrderingGuide,
"delivery":Deilivery,
"history":History
}},
router-滚动行为:
在main.js中:
const router=new VueRouter({
routes,
mode:'history',
scrollBehavior(to,from,savedPosition){
//return {x:0,y:200}
//return {selector:'.btn'}
if(savedPosition){
return savedPosition;
}else{
return {x:0,y:0}
}
}
})
跨域请求
首先需要在项目目录下/config/index.js中的proxyTable: 属性中加入:
proxyTable: {
'/apis': {
// 测试环境
target: 'http://jsonplaceholder.typicode.com', // 接口域名
changeOrigin: true, //是否跨域
pathRewrite: {
'^/apis': '' //需要rewrite重写的,
}
}
}
然后在需要执行跨域请求的.vue中使用created()钩子函数(fetch方式)
created(){
//fetch:如果有参数,则要在method下加:body:JSON.Stringify({xx:xx,xx,xx})
fetch("/apis/users",{ //该路径为在index.js中配置的
method:"get"
}).then(result=>{
//console.log(result);
return result.json()
}).then(data=>{
console.log(data)
})
}
axios方式:(主流的http请求库)
先用npm install axios
安装,然后在main.js中引入并配置:
import axios from 'axios'
//全局配置 有值的话就在路径上自动加上
axios.defaults.baseURL=''
//axios请求的header中有token时:
axios.defaults.headers.common['token']="xxxxx"
//设置请求头
axios.defaults.headers.post[content-type]="application/json"
axios.defaults.headers.get['Accepts']="application/json"
//可全局使用配置
Vue.prototype.$axios=axios 第一种 不同之处:resource返回的在body中。axios是在data中,且只支持ES6的箭头函数
Vue.prototype.$http=axios 第二种
在需要跨域请求的.vue文件中使用create()钩子函数:
create(){
//axios 如果有参数,直接在路径后加逗号,
this.$axios.get("/apis/users",{params:{'id':1}}) //该路径为index.js中配置的
.then(data=>{
console.log(data)
})
}
传参对应后台接口:
axios get请求方式 传递给后台的参数都是字符串下形式,无法传递json对象 或数组对象等
post请求方式则可以实现,
//java后台接口
@RequestMapping("/getOne")
public User getUserById( int id){//此时参数为单个属性
return userService.selectByPrimaryKey(id);
}
//对应的axios:{params:{变量名:值}}格式不可变
created() {
this.$axios.get('/apis/user/getOne',{params:{id:1}})
.then(data=>{
console.log(data);
this.userz=data.data;
}),
//后台接口为对象的情况:
@PostMapping("/login")
public User login(@RequestBody User user){//最好以json形式接收
System.out.println(user.getUsername());
return userService.login(user);
}
//对应的axios://post自动将{}对象转换为json,无需考虑后台接口变量名
this.$axios.post('/apis/user/login',{username:"jim",password:"123"})
.then(data=>{
console.log(data);
this.user2=data.data;
})
使用vue-resource的get请求本地json文件(官方停止更新)
将json放在项目目录下的static中,再进行访问。
created(){
this.$http.get('./../static/posts.json')
.then(data=>{
//console.log(data);
this.blogs=data.body.slice(0,10);
console.log(this.blogs)
})
}
自定义指令:(全局) 目的:绑定dom
在main.js中:
//自定义指令1:
Vue.directive('rainbow',{
bind(el,binding,vnode){
el.style.color='#'+Math.random().toString(16).slice(2,8); 产生16位随机数,截取6位,实现彩虹色
}
})
//自定义指令2:
Vue.directive('theme',{
bind(el,binding,vnode){
if(binding.value=='width'){
el.style.maxWidth='1200px'
}else{
el.style.maxWidth='560px'
}
if(binding.arg=='column'){
el.style.background="#6677cc";
el.style.padding="20px";
}
}
})
在vue中对应:
<template>
<div v-theme:column="'width'" id="show-blogs"> //2字符串+'',对象+{},数组+[]
<h1>博客总览</h1>
<div v-for="blog in blogs" class="single-blog">
<h2 v-rainbow>{{blog.title}}</h2> //自定义1
<article>{{blog.body}}</article>
</div>
</div>
</template>
自定义指令:(组件内)
直接在<script>中写:
directives:{
"rainbow":{
bind(el,binding,vnode){
el.style.color="#"+Math.random().toString(16).slice(2,8);
}
}
}
获取dom元素:
- 救命稻草,因为前端框架就是为了减少dom操作,最不济也是用自定义指令去绑定dom元素,但再特定情况下就不行了:
- 在指定的元素上加
ref="名称A"
- .在获取的地方加
this.$refs.名称A
- 如果ref放在原生DOM元素上,获取的数据就是原生DOM对象
- 如果ref放在组件对象上,获取的就是组件对象
template中:
原生DOM对象:<div ref="ddv"></div>
组件DOM对象:<babby ref="babby"></babby>
script中:
mounted() {
//原生DOM对象:
console.log(this.$refs.ddv);
this.$refs.ddv.innerHTML="sdsds";
//组件DOM对象
console.log(this.$refs.babby.$el);
this.$refs.babby.$el.innerHTML='sdsdsd';
},
自定义过滤器(全局)
在main.js中:
//自定义过滤器1
Vue.filter("touppercase",function(value){
return value.toUpperCase();
})
//自定义过滤2
Vue.filter("snipet",value=>{
return value.slice(0,100)+".....";
})
对应vue:
<div v-for="blog in blogs" class="single-blog">
<h2 v-rainbow>{{blog.title | touppercase}}</h2> //对应管道
<article>{{blog.body | snipet}}</article>
</div>
自定义过滤器(组件内)
直接在<script>中写:
filters:{
//普通语法
"to-uppercase":function(value){
return value.toUpperCase();
},
//ES6语法
toUppercase(value){
return value.toUpperCase();
}
}
路由参数:
目的是实现类似于根据id查1条数据:
实现方式:和一般的路由配置方式一致,只是在path中+:以解析参数:
{path:'/blog/:id',component:SingleBlog}
在vue中接收:用this.$route.params.xx:
<script>
export default{
name:"single-blog",
data(){
return{
id:this.$route.params.id,
blog:{}
}
},
created() {
this.$http.get("http://jsonplaceholder.typicode.com/posts/"+this.id)
.then(data=>{
this.blog=data.body;
})
}
}
</script>
实现点击标签变色:
<router-link exact to="/">博客</router-link>
<router-link exact to="/add">写博客</router-link>
.router-link-active{ /* 配合标签上的exact属性实现动态改变 */
background: rgba(255,255,255,0.8);
color: #444;
}
安装JSONServer:
终端键入:npm install -g json-server
下载完成任意建一个文件夹,cd进入,键入npm install
或npm install --yes
随后该文件夹下会有package.json
安装json server模块:npm install json-server --save
默认的jsonServer启动方式:json-server --watch db.json
或配置jsonServer启动方式:
{
"name": "jsonserver",
"version": "1.0.0",
"description": "test",
"main": "index.js",
"scripts": {
"json:server":"json-server --watch db.json" //配置后的
},
"author": "",
"license": "ISC",
"dependencies": {
"json-server": "^0.14.2"
}
}
配置后为:npm run json:server
get请求:
//获取所有用户信息
localhost:3000/users
//获取id为1的信息
http://localhost:3000/users/1
//获取公司信息
localhost:3000/companies
localhost:3000/companies/1
//获取公司id为1的用户信息
localhost:3000/companies/1/users
//名字为x,id为2的公司
http://localhost:3000/companies?name=Microsoft&id=2
//名为xxx的公司
http://localhost:3000/companies?name=Microsoft&name=Apple&name=Google
//一页中显示两个数据
http://localhost:3000/companies?_page=1&_limit=1
//降序排序:
http://localhost:3000/users?_sort=id&_order=desc
//年龄大于30de
http://localhost:3000/users?age_gte=34
//年龄zai32和40之间:
http://localhost:3000/users?age_gte=32&age_lte=40
//在uers中匹配
http://localhost:3000/users?q=tom
使用vuex进行数据管理
使用vuex进行数据管理需要:cnpm install vuex --save
然后在项目中src下新建文件夹:store,创建store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
strict:true,//严格模式
state:{
products:[
{name:"马云",price:200},
{name:"马化腾",price:140},
{name:"马冬梅",price:20},
{name:"马蓉",price:10}
]
},
getters:{
saleProducts:(state)=>{
var sale=state.products.map(product=>{
return {
name:"**"+product.name+"**",
price:product.price/2
};;
});
return sale;
}
},
mutations:{
reduce:(state,amount)=>{
state.products.forEach(function(product){
product.price-=1;
})
}
},
actions:{
//context相当于是this.$store
//payload:用于接收参数
reduced:(context,payload)=>{
setTimeout(function(){
context.commit('reduce',payload);
},3000)
}
}
})
然后在main.js中引入:
import Vue from 'vue'
import App from './App.vue'
import {store} from './store/store'
new Vue({
store:store,
el: '#app',
render: h => h(App)
})
最后在.vue文件中使用:
在<script>中加入computed属性,将我们要使用的数据封装成方法,最后return this.$store.state.products;
完整代码:
<template>
<div id="product-list-one">
<h2>product-list-one</h2>
<ul>
<li v-for="product in saleProducts">
<span>{{product.name}}</span>
<span class="price">{{product.price}}</span>
</li>
</ul>
<button @click="reduce(4)">商品降价</button>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
import {mapActions} from 'vuex'
export default {
name: 'app',
computed:{
/* products(){
return this.$store.state.products;//触发state 不推荐直接访问数据。
},*/
saleProducts(){
return this.$store.getters.saleProducts;//触发getters
}
/* 使用辅助函数
...mapGetters([
"saleProducts" //触发getters
])
*/
},
methods:{
reduce:function(mount){
//常规写法:严格模式下不执行
/* this.$store.state.products.forEach(product=>{
product.price-=1;
}) */
//vuex的写法 Mutations
//this.$store.commit('reduce');//触发mutation
//使用dispatch调用mutation
this.$store.dispatch("reduced",mount);//触发action
/* 使用辅助函数
...mapActions([
"reduced" //触发action
])
*/
}
}
}
</script>
其中state的使用场景就是用于管理全局数据
getters的使用场景是在每个vue组件中同步数据的变化而不用在每个组件中去写重复的方法去保证数据的一致性
mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Action 提交的是 mutation,而不是直接变更状态。可以传参数
state:存储数据 (用在computed中,但不推荐直接用)
getters:获取数据(调用state) (用在computed中)
mutations:修改数据(调用state) (用在methods中,但不推荐直接用)
actions:异步调用mutations (用在methods中)
使用辅助函数调用getters和actions
先import,再…mapGetters([])或…mapActions([]),代码在上文注释提及。
vueBus实现父子组件通信:
模拟爸爸等儿子电话的场景,
先来new个电话(中间件):conector.js
import Vue from 'vue'
var conector = new Vue()
export default conector
再来new父亲拿着电话等儿子联系 daddy.vue
<template>
<button @click="listen">爸爸焦急的等儿子电话</button>
</template>
<script>
import conector from '../conector.js'
export default{
methods:{
listen(){
conector.$on('phone',msg=>{
console.log(msg);
})
}
}
}
</script>
最后new儿子打电话:
<template>
<button @click="call">打电话</button>
</template>
<script>
import conector from '../conector.js'
export default{
methods:{
call(){
conector.$emit('phone',"打个电话报平安")
}
}
}
</script>
先conector.$on(),再conector.$emit,才可实现通信
附:
饿了吗的ui
element-ui(pc端)
mint-ui(移动端)
wappalyzer:获取网站技术的xx