vue学习笔记

269 阅读8分钟

前言

小弟初入前端这是小弟的学习笔记和总结,不适合学习使用;如有大佬发现错误还望指出,在此谢过。

跨域解决

1.vue-cli4创建项目

在根目录下的webpack.config.js文件中添加代码

...   //省略代码webpack配置文件
module.exports={
    ... //省略代码webpack配置进、出口文件
    devServer:{
        ... //省略代码webpack
        proxy:{
            '/app':{
                target:'http://www.weather.com',//要代理的地址
                changeOrigin:true,
                pathRewrite:{
                '^/app':''
            }
            },
            '/api':{
                    target:'http://www.baidu.com',//要代理的地址
                    changeOrigin:true,
                    pathRewrite"{
                    '^/app':''
                   }
             }
        }
    }
}

2.vue-cli3创建项目

Config文件夹下的index.js文件

...   //省略代码webpack配置文件
module.exports={
    dev:{
        ... //省略代码webpack
        proxyTable:{
            '/app':{
                target:'http://www.weather.com',//要代理的地址
                changeOrigin:true,
                pathRewrite:{
                '^/app':''
            }
            },
            '/api':{
                    target:'http://www.baidu.com',//要代理的地址
                    changeOrigin:true,
                    pathRewrite"{
                    '^/app':''
                   }
             }
        }
    }
     ... //省略代码webpack配置进、出口文件
}
**重新启动服务器**

3.生产环境

 把dist目录复制到nginx下的html文件夹下面 , 更改conf文件夹下nginx.conf文件如下图

...   //省略代码
server{
	listen    8082;   //端口号
	server_name   localhost;   //ip地址
    root   D:/nginx-1.17.5/html/dist;  //打包生产文件夹位置
    index   index.html;   //打开html文件
    ...   ////省略代码
    location /api/{
        proxy_pass http://127.0.0.1:3000/;
        add_eader Content_Type "teXt/plain;charset = utf_8";
        add_header "Access_Control_Allow_Origin" '*';
        add_header "Access_Control_Allow_Credentials" 'true';
        add_header "Access_Control_Allow_Methods" 'GET','POST';
    }      //如果多个在此处继续写
   ...   //省略代码 
}

打包上线

命令行输入:npm run build

打包出来后项目中就会多了一个文件夹dist,这就是我们打包过后的项目。

第一个问题:

文件引用路径。我们直接运行打包后的文件夹中的index.html文件,会看到网页一片空白,f12调试,全是css,js路径引用错误的问题。

解决:到config文件夹中打开index.js文件。文件里面有两个assetsPublicPath属性,更改第二个,也就是更改build里面的assetsPublicPath属性:assetsPublicPath: './'

assetsPublicPath属性作用是指定编译发布的根目录,‘/'指的是项目的根目录 ,'./'指的是当前目录。

第二个问题:

router-view中的内容显示不出来。路由history模式。这个坑是当你使用了路由之后,在没有后端配合的情况下就手贱打开路由history模式的时候,打包出来的文件也会是一片空白的情况,

解决 : 在 router.js 中将 mode: history注释掉

背景图片引入问题:

img标签引入一般不会有问题,但通过css引入的背景图片或者js引入的就会出现路径问题

解决方法:

找到根目录下build文件中的utils.js文件,搜索ExtractTextPlugin.extract,并为其添加属性和值为 publicPath: '../../',如下图

if (options.extract) {
  return ExtractTextPlug4.extract({
    use:loaders
    publicPath:'../../',
    faliback: 'vue-style-loadec'
  })
 } 

通过判断绑定那个数据

<div :icon="marker===1 ? icon2:icon3"><div>

百度地图点的图标(icon)使用本地图片

data(){
  return {
     icon1:{url:require('../../ass/img/dw.png'),size:{width:300,heigth:157}}
  }
}

子父组件传值

父组件传值

 <pop v-if='pop' :father='src' @srcChange="handleSrcChange">
 <script>
	exprort default{
	 ... //省略代码
	 methods:{
	    handleSrcChange:function (e){
	     //父组件自定义事件。参数e为子组件传递过来的参数
         console.log(e)

	    }
	 }
   }
</script>

子组件接收

<template>
	{{father}}
	<div class="dsd" @click="baseHidden"></div>
</template>
<script>
	exprort default{
    	props:['father']
         ... //省略代码
         methods:{
            baseHidden:function (){
             //子向父传递数据通过触发事件
             // this.$emit("父组件自定义的事件名称","要传递的参数")
            this.$emit("srcChange""this.src") 
            }
         }
    } 
</script>

//传参校验
props: {
    // 基础的类型检查 (`null` 匹配任何类型)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }

如果父组件传递图片路径,应以父组件的位置写

VueX使用

第一步:安装:npm install vuex --save

第二步:在src目录下创建一个store文件夹并在store文件夹下创建一个index.js文件

文件内容如下

import Vuex from'vuex';
import Vue from 'vue';
 Vue.use(Vuex);
//创建仓库
 const store Vuex.Store({
   //数据
       state: {
          test_data: “this is some test data”,
          color: “light-green”
        },
        //修改值得方法
         mutations: {
           setColor(state, coLor) {
               state.color= color;
           },
          Action:{
              login({commit}, username) {
                return new Promise((resolve, reject) => {
                	setTimeout(() => {
                		if (username === 'admin') {
                			commit('setColor',res.data.data)//调用上方mutations中setColor方法修改数据
                			resolve()
                		} else {
                			reject()
                		}
                	}, 1000);
                })
              }
          },
          modules: {//模块化使用modules定义多个子模块利于组件复杂状态
            user,
          }
  }
export default store

第三步:main.js加载和挂载

import stort from  './store/index'
new Vue({
   el: ‘#app’, 
   router,
   store,
   components: { App }, 
  template: ‘<App!>’
})

第四步:使用

① 修改

//第一个参数为mutations下的方法,二为参数
//不用加载任何vuex的文件main.js中已经全局加载了
this.$store.commit('setColor',res.data.data)

② 使用

this.$store.state.color

③Action派发

this.$store.dispatch('login', 'admin').then(() => {
    this.$router.push(this.$route.query.redirect)
}).catch(() => {
    alert('用户名或密码错误')
})

④模块化

user.js 定义

export default {    
    namespaced: true, // 避免命名冲突    
    // ...
}  

访问方式响应变化

// Login.vue
<button @click="login" v-if="!$store.state.user.color">登录</button>  //多上一个文件名$store.state.user.color和$store.state.color
this.$store.dispatch('user/login', 'admin').then(() => {
    const redirect = this.$route.query.redirect || '/'
    this.$router.push(redirect)
}).catch(() => {
	alert('用户名或密码错误')
})

路由携带参数

路由页面是设置

<router-link :to="{path:'/xiangx',query:{a:atm.a}}"></router-link>   //也可以写成to="/xiangx?a=1"

目标页设置

getImgUrl(){    
    console.log(this.$route.query.a)//获取参数的值
}

请求

进入项目,npm安装npm install axios --save

在main.js下引用axios

import axios from 'axios';      
Vue.prototype.$axios=axios;  

使用

 this.$axios.post('/api/students',{  
     params: {  name:"admin", pass:123123},     //参数 
}).then(res => { console.log(res)      //请求成功后的处理数
}).catch(err => { console.log(err)     //请求失败后的处理数 
})    
//有请求头
this.$axios({url:'/app/index',method: 'post',
             data: { _csrf: sessionStorage.getItem('csrfToken'), msg:this.msg, typeid:this.typeid,jiajei:this.jiajei,neir:this.neir},     //参数
             headers: {'x-csrf-token': sessionStorage.getItem('csrfToken')}
        }).then(res => { console.log(res)      //请求成功后的处理数
        }).catch(err => { console.log(err)     //请求失败后的处理数
        })
      }

扩展优化

优化方案 一

// api.js
 /* 登入页面获取公钥 */
export const getPublicKey = (data) => {
    return this.$axios.post({ url: '/userGateway/user/getPublicKey' }, data)
}

// 用户登录
export const login = data => {
    return this.$axios.post({ url: '/userGateway/userSentry/login' }, data)
}

// 验证码登录
export const loginByCode = data => {
    return this.$axios.post({ url: '/userGateway/userSentry/loginByCode' }, data)
}

在组件中使用接口:

<script>
import { getPublicKey } from './config/api.js'
export default {
    mounted() {
        getPublicKey().then(res => {
            // xxx
        }).catch(err => {
            // xxx
        })
    }
}
</script>

深度优化

// api.js
const apiList = [
    '/userGateway/user/getPublicKey',  // 登入页面获取公钥
    '/userGateway/userSentry/login',  // 用户登录
    '/userGateway/userSentry/loginByCode',  // 验证码登录
]

let apiName, API = {}
apiList.forEach(path => {
    // 使用正则取到接口路径的最后一个子串,比如: getPublicKey
    apiName = /(?<=\/)[^/]+$/.exec(path)[0]
    API[apiName] = (data) => {
        return this.$axios.post({url: path}, data)
    }
})
export { API }

在组件中使用接口:

<script>
import { API } from './config/api.js'
export default {
    mounted() {
        API.getPublicKey().then(res => {
            // xxx
        }).catch(err => {
            // xxx
        })
    }
}
</script>

Vue-cli 4以上版本修改

将router下的index.js中的mode设置成hash,不要设置成history

在vue.config.js文件中加入如下配置:

module.exports = { 
  devServer : { 
     open:true, 
     port:8089, 
     host:"127.0.0.1",      
      proxy:{    // 设置代理
        '/api':{ 
             target:'', 
             changeOrigin:true, 
             pathRewrite:{ 
                '^/api':'' 
             } 
          } 
      } 
   }, 
   publicPath:'./', 
   outputDir:'dist', 
   assetsDir:'static' 
}
module.exports = { 
 publicPath: "./", // 公共路径(必须有的) 
 outputDir: "dist", // 输出文件目录  
 // 静态资源存放的文件夹(相对于ouputDir) 
 assetsDir: "static", 
 // eslint-loader 是否在保存的时候检查(果断不用,这玩意儿我都没装) 
 lintOnSave:false, 
 // 我用的only,打包后小些 
 runtimeCompiler: false, 
 productionSourceMap: true, // 不需要生产环境的设置false可以减小dist文件大小,加速构建  
 devServer: { 
  open: true, // npm run serve后自动打开页面 
  host: '0.0.0.0', // 匹配本机IP地址(默认是0.0.0.0) 
  port: 8080, // 开发服务器运行端口号 
  proxy: null, 
 }, 
} 

最全的

 module.exports = { 
   publicPath: "./",  // 公共路径(必须有的) 
   outputDir: "dist",     // 输出文件目录
   assetsDir: "assets",   // 静态资源存放的文件夹(相对于ouputDir) 
   lintOnSave:false,    // eslint-loader 是否在保存的时候检查(果断不用,这玩意儿我都没装)
   compiler: false,    // 我用的only,打包后小些  
   productionSourceMap: true, // 不需要生产环境的设置false可以减小dist文件大小,加速构建  
     // css: {   // css相关配置(我暂时没用到)
     // extract: true,   // 是否使用css分离插件 ExtractTextPlugin 
     // sourceMap: false, // 开启 CSS source maps? 
     // loaderOptions: {},  // css预设器配置项 
     // modules: false   // 启用 CSS modules for all css / pre-processor files. 
     // }, 
    devServer: {   // webpack-dev-server 相关配置
      open:true, // npm run serve后自动打开页面 
      host: '0.0.0.0', // 匹配本机IP地址(默认是0.0.0.0) 
      port: 8080, // 开发服务器运行端口号 
      proxy: null, 
      // 注:目前本项目暂时没有写后台接口,没有跨域问题,暂时不配置proxy 
    }, 
} 

全局变量(例如请求地址的域名

(1)引用组件实现全局变量

新建js文件,将全局变量暴露出来,在需要用到全局变量的地方引入。

import common from "../commom.js; //引入js文件   
var serse=common.server 

(2)main.js文件挂载实现全局变量

main.js文件中如下设置

vue.prototype.server="https://www.imovit.com/superhero";//网站地址为全局变量  

在需要使用此全局变量的地方this.server就可以

Vue实例的生命周期函数和属性

(1)生命周期函数

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

**beforeCreate**:实例刚在内存中被创建出来,创建出来的只是一个空的Vue实例,此时,还没有初始化好 data 和 methods 属性,data和methods都还不能使用
**created**:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板。也是最早的,只能在created中进行相关的操作
**beforeMount**:此时已经完成了模板的编译,但是还没有挂载到页面中,也就是说没有渲染到页面中
**mounted**:此时,已经将编译好的模板,挂载到了页面指定的容器中显示,已经将数据渲染到页面中了;当执行完mounted就表示,实例已经被完全创建好了,此时,如果没有其他操作的话,这个实例,就静静地躺在内存中,一动不动;如果要通过某些插件操作页面上的DOM节点,最早要在mounted中进行;只要执行完了mounted,就表示整个Vue实例已经初始化完毕了,此时,组件已经脱离了创建 阶段;进入到了运行阶段

运行期间的生命周期函数:

**beforeUpdate**:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的数据还是旧的,此时还没有开始重新渲染到DOM节点

updated:实例更新完毕之后调用此函数,此时 data 中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

销毁期间的生命周期函数:

**beforeDestroy**:实例销毁之前调用。当执行beforeDestory钩子函数的是时候,Vue实例就已经从运行阶段,进入到了销毁阶段;但是实例身上所有的data,methods,以及过滤器,指令等都处于可用状态,此时还没有真正的执行销毁过程,只是为销毁实例做准备
**destroyed**:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。组件就已经被完全销毁了,此时组件中所有的数据,方法,过滤器,指令等都已经不可用了

(2)实例属性

methods :事件方法执行 例:methods:{}

watch :监听一个值的变化,执行相应函数

computed :计算属性

filters :注册过滤器

components :组件挂载

directives :自定义指令

路由守卫

(1)设置和获取localStorage

localStorage.setItem('Authorization', "123"); 
alStorage.getItem('Authorization'); 

(2)设置路由守卫

import Vue from 'vue'  
import Router from 'vue-router'  
import HelloWorld from '@/components/HelloWorld'  	  
Vue.use(Router)   
 const router =new Router({  
  routes: [  
    {  
      path: '/',  
      name: 'HelloWorld',  
      component: HelloWorld  
    },{  
        path: '/home',  
        component: resolve => require(['../components/home.vue'], resolve),  
    },{  
      path: '/alarm',  
        component: resolve => require(['../components/alarm.vue'], resolve),  
    },{  
      path: '/baidu',  
        component: resolve => require(['../components/baidumap.vue'], resolve),  
    },{  
      path: '/',  
        component: resolve => require(['../components/HelloWorld.vue'], resolve),  
    }  
  ]  
});  
 router.beforeEach((to, from, next) => {  
    if (to.path === '/') {  
    next();  
  } else {  
    let token = localStorage.getItem('Authorization');  
    if (token === null || token === '') {  
      next('/');  
    } else {  
      next();  
    }  
  }      
});  
export default router;

watch使用

在vue中watch是用来响应数据的,watch的写法有三种

①最简单最常用的

<input type="text" v-model="cityName"/>

new Vue({
	el:"#root",
	data:{
		cityName:'shanghai'
	},
	watch:{
		cityName(newName,oldName){
		   ....
		}
	}
})
//也可以直接加字符串的方法名
//watch:{
//		cityName:'nameChange'
//	}

② immediate和handler

监听的数据写成对象的形式,包含handler函数和immediate,immediate默认为flaset,第一次绑定时不会执行函数,值改变时才执行。

new Vue({
	el:"#root",
	data:{
		cityName:'shanghai'
	},
	watch:{
		cityName:{
			handler(newName,oldName){
               ....
            },
            immediate:true    //watch声明时就立即执行
		}
	}
})

③ deep 深度监听

<input type="text" v-model="cityName"/>

new Vue({
	el:"#root",
	data:{
		cityName:{id:1,name:'shanghai'}
	},
	watch:{
		cityName:{
			handler(newName,oldName){
               ....
            },
            immediate:true,    //watch声明时就立即执行
            deep:true    //给cityName的所有属性都加上监听,任何一个变化都会执行handler函数
		}
	}
})

//如果只想监听对象中的一个属性
	watch:{
		'cityName.name':{
			handler(newName,oldName){  /*....*/},
            immediate:true,    //watch声明时就立即执行
            deep:true    //给cityName的所有属性都加上监听,任何一个变化都会执行handler函数
		}
	}

插槽

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。

①默认插槽

//子组件
<template>
    <div>
        <h1>今天天气状况:</h1>
        <slot></slot>  //子组件中挖坑
    </div>
</template>
//父组件
<template>
    <div>
        <child><div>多云</div></child>//父组件中填坑
    </div>
</template>

②具名插槽

//子组件
<template>
    <div>
        <div class="header"><slot name="header"></slot></div>//我是页头的具体内容
        <div class="defond"><slot></slot></div>  //我是默认内容
        <div class="footer"><slot name="footer"></slot></div>//我是页尾的具体内容
    </div>
</template>
//父组件
<template>
  <div>
    <child1>
        <template slot="header"><p>我是页头的具体内容</p> </template>
        <template slot="footer"> <p>我是页尾的具体内容</p></template>
		<template ><p>我是默认内容</p></template>
    </child1>
   </div>
</template>

注意:

父级的填充内容如果指定到子组件的没有对应名字插槽,那么该内容不会被填充到默认插槽中。

如果子组件没有默认插槽,而父级的填充内容指定到默认插槽中,那么该内容就“不会”填充到子组件的任何一个插槽中。

如果子组件有多个默认插槽,而父组件所有指定到默认插槽的填充内容,将“” “全都”填充到子组件的每个默认插槽中。

<template>
  <div id="app">
    <hand></hand>

  </div>
</template>

<script>
import Hand from './components/HelloWorld.vue'
export default {
  name: 'App',
  data () {
    return {

    }
  },
  components: {
      Hand
  },
  mounted(){
  },
  methods: {
  }
}
</script>

<style scoped>
html,body,#app {
  width: 100%;
  height: 100%;
  margin: 0;
  background-color: #f6f6f6;
}

</style>