Vue2.0个人笔记

发布于:2022-12-26 ⋅ 阅读:(883) ⋅ 点赞:(0)

WebPack打包工具

webPack配置文件

创建一个webpack.config.js的打包配置文件
//webpack.config.js文件:
module.exports={
项目入口配置,
项目出口配置,
加载器配置,
插件配置,
开发模式配置,
其他
}
项目入口配置

    //1.直接配置入口文件
    module.exports = {
    entry: './path/to/my/entry/file.js', //输出时默认为main.js
    };

    //2.配置基础目录,然后配置入口文件
    module.exports = {
    context: __dirname+'/app', 
    entry:"./test.js"  //相对于context的路径 
    };

项目出口配置

    module.exports = {
	mode: "none",
	context: __dirname + '/app',
	// entry:'./test.js',
	entry: {
		a: './test.js'
	},
	output: {
		asyncChunks: true, //创建按需加载的异步 chunk。
		
		path:__dirname+"/dist2",//输出的目录,绝对路径,默认dist
		
		// filename: 'bundle.js', //输出的文件名
		filename: '[name]-666-[id]bundle[hash:5].js', //输出的文件名,[hash:5]为5位哈希值,[id]为打包的chunk的id,[name]为入口的属性名,缺省则为mian,这几个一定记住 vue和react的css作用域-就是这个几个设计的
			
		// library: 'hqyj',//库名
		library: {
			name: 'MyLibrary', //库名
			type: 'var', //配置将库暴露的方式。('var'、'module'、'assign'、'assign-properties'、'this'、'window'、'self'、'global'、'commonjs'、'commonjs2'、'commonjs-module'、'commonjs-static'、'amd'、'amd-require'、'umd'、'umd2'、'jsonp' 以及 'system')
		},
		// libraryTarget: 'umd',//配置如何暴露 library,优先级比library高但是:[请使用 output.library.type 代理,因为我们可能在未来放弃对 output.libraryTarget 的支持。]
		
		auxiliaryComment: 'Test Comment', //各种模块化导出技术的统一注释(把type设置为umd)	  
		//各种模块化导出技术的分别注释(webpack允许你的项目使用各种模块化技术 它都可以识别并打包)
		// auxiliaryComment: {
		// 	root: 'Root Comment',
		// 	commonjs: 'CommonJS Comment',
		// 	commonjs2: 'CommonJS2 Comment',
		// 	amd: 'AMD Comment',
		// },	

		clean: true, // 在生成文件之前清空 output 目录
		// clean: {
		//       dry: true, // 小黑窗打印而不是删除应该移除的静态资源
		//     },
		// clean: {
		// 	keep: /ignored\/dir\//, // 保留 'ignored/dir' 下的静态资源不删
		// 	// keep(asset) {
		// 	// 	return asset.includes('ignored/dir');//同上
		// 	// },
		// },		
	}
};

加载器loader:当打包时,如果文件中引用了其他文件时,就对该文件进行编译,编译为浏览器能够读懂的文件(HTML,CSS,JS)
css-loader&style-loader
css-loader:解析css文件,将css文件引入到js文件中
style-loader:将js文件中引入的css文件挂载到HTML页面的head中
npm install css-loader style-loader --save-dev

    module:{
		   rules:[
			   {test:/\.css$/,use:["style-loader","css-loader"]}
               //use数组的加载是从后往前加载;test:正则表达式,以CSS结尾的文件
		   ]
	   }

外部引入CSS,单独形成一个css文件,进行引入
npm i mini-css-extract-plugin --save-dev

    const MiniCssExtractPlugin = require("mini-css-extract-plugin")
    module: {
		rules: [
			//运行bundle时直接以style标签形式动态写入到页面
			// {test:/\.css$/,use:["style-loader","css-loader"]},
			{
				test: /\.css$/,
				//css生成到css文件中,项目中以<link>形式引入使用
				use: [MiniCssExtractPlugin.loader, 'css-loader']
			}
		]
	},
	plugins: [
		new MiniCssExtractPlugin({
			filename: '[hash].css' //css生成到css文件中,项目中手动以<link>形式引入使用
		})
	]

file-loader:引入了常见类型(好多类型, 比如视频音频图片等等媒体编码)的文件,可以使用file-loader将其打包
npm i file-loader --save-dev

    module: {
	rules: [
		//直接以style标签形式动态写入到页面
	    {test:/\.css$/,use:["style-loader","css-loader"]},
        //项目中引入了以下结尾的文件就会启动file-loader
		{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
	]
    ,

ts-loader:将TS编译为js
npm install ts-loader webpack typescript --save-dev
项目中需要新建一个tsconfig.json文件(这是ts编译需要的配置文件)

    module: {
		rules: [
			//直接以style标签形式动态写入到页面
			// {test:/\.css$/,use:["style-loader","css-loader"]},
			{
				test: /\.css$/,
				//css生成到css文件中,项目中以<link>形式引入使用
				use: [MiniCssExtractPlugin.loader, 'css-loader']
			},
			{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
			{ test: /\.ts$/, use: 'ts-loader' },
		]
	},

babel-loader:将项目中的高级语言转为低版本的js,例如:TS=>js,es6=>js
npm install babel-loader @babel/core @babel/preset-env webpack -D

    module: {
		rules: [
			//直接以style标签形式动态写入到页面
			// {test:/\.css$/,use:["style-loader","css-loader"]},
			{
				test: /\.css$/,
				//css生成到css文件中,项目中以<link>形式引入使用
				use: [MiniCssExtractPlugin.loader, 'css-loader']
			},
			{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
			{ test: /\.ts$/, use: 'ts-loader' },
			//exclude:/node_module/ 意思是下载的模块不转码,其他js文件转码
			 {
			      test: /\.js$/,
			      exclude: /(node_modules|bower_components)/,
			      use: {
			        loader: 'babel-loader',
			        options: {
			          presets: ['@babel/preset-env'],
			          plugins: ['@babel/plugin-proposal-object-rest-spread']
			        }
			      }
			    }
		]
	},

sass-loader
npm install sass-loader sass webpack --save-dev

    module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [        
          'style-loader',  //不用单独引入 直接运行js代码 动态写入页面    
          'css-loader',
          // 将 Sass 编译成 CSS
          'sass-loader',
        ],
      },
    ],
  },

插件plugin
插件的引入,通过require(),然后把插件添加到 plugins 数组中

EslintWebpackPlugin:语言查错工具
eslint-webpack-plugin插件就是使用 eslint 来查找和修复 JavaScript 代码中的问题。

eslint-webpack-plugin 3.0 仅支持 webpack 5。对于 webpack 4 请查看 2.x 分支

环境准备:
npm install eslint-webpack-plugin --save-dev
如果未安装 eslint >= 7 ,你还需先通过 npm 安装:
npm install eslint --save-dev

插件配置:

    const ESLintPlugin = require('eslint-webpack-plugin');

    module.exports = {
  // ...
  plugins: [new ESLintPlugin(options)],
  // ...
    };


    //options的参数配置:
    context:__dirname+"/src" //指定文件根目录
    extensions:String|Array[String] //指定需要检查的扩展名.不配置默认为"js"
    exclude:"node_modules" //指定需要排除的文件及目录。必须是相对于 options.context 的相对路径。
    files:String|Array[String] //指定目录、文件或 globs ,必须是相对于 options.context 的相对路径。 如果是目录则递归查找所有匹配 options.extensions 选项的文件。 

    //测试时在main.js中故意写个错误语法:
    let a=20;
    let a=90;

HtmlWebpackPlugin:简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于那些文件名中包含哈希值,并且哈希值会随着每次编译而改变的 webpack 包特别有用。你可以让该插件为你生成一个 HTML 文件,使用 lodash 模板提供模板,或者使用你自己的 loader
npm install --save-dev html-webpack-plugin

    const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: 'index.js',
  output: {
    path:__dirname+'/dist',
    filename: 'bundle.js',
  },
  plugins: [new HtmlWebpackPlugin()],
};
//该插件将为你生成一个HTML5 文件, 在 body 中使用 `script` 标签引入你所有 webpack 生成的bundle.js。 
//但是每次生成的模板都是新的 

可以在参数中进行配置:https://github.com/jantimon/html-webpack-plugin#options
开发环境devServer
安装:npm install --save-dev webpack webpack-dev-server webpack-cli
配置参数: https://webpack.js.org/configuration/dev-server/
配置好以后小黑窗的服务启动指令: webpack server:没有打包到磁盘中

    module.exports = {
    devServer: {
		//告诉dev-server在服务器启动后打开浏览器。将其设置为true以打开默认浏览器。但是:如果你的电脑有防火墙或者有管理员权限的限制或者电脑很卡 可能不会自定打开浏览器
		open: true, //cli中 webpack serve --open					
						
		//本地服务器监听的端口
		port: 8080,
		
		//启用热更新,直接使用webpack的热更新模块.就是改了代码自动打包后自定刷新浏览器页面:bug就是同open,记住 卡了就刷新一下不用纠结
	    hot: true,
		
		//指定要使用的主机(localhost|ipv4|ipv6)。如果你想让你的服务器可以被外部访问,像这样指定:
        //让你同桌访问 然后你改项目代码 他就会刷新
		host: '192.168.2.60',
		
		//启用gzip压缩
		compress: true,
			
		//代理配置,这里只是配置,不用写代理服务器的代码(配置好了它帮我们实现)
		proxy: {
			'/api': 'http://localhost:7001',
		},
		 // proxy: {
		 //      '/api': {
		 //        target: 'http://localhost:7001',
         //        secure:true,//如果代理的target是https接口,需要配置它 
		 //        pathRewrite: { '^/api': '/chongxie' },//请求时重写pathname
		 //      },
		 //    },
	   }		
	}

cli指令:在小黑窗中直接带参数 可以进行打包配置:webpack --watch
cli指令:在小黑窗中直接带参数 可以进行打包配置:webpack server --config xxxconfig.js 打包的时候启动该配置文件

Vue

Vue引入方式

第一类引入方式

CDN引入
直接在script标签中的src填入Vue的CDN链接
webpack打包引入:通过把Vue.js文件引入到项目文件中然后打包

	//vue 2.0
	import Vue from './vue.js'
	new Vue({
		el: "#app",
		data: {
			msg: "hello",
			age:"18"
		}
	})	
	//vue 3.0
	import Vue from './vue.js'
	Vue.createApp({ 
		data() {
		let msg = "hello";
   		return {
      		msg
    		}
  			}
	}).mount(#app)

编辑器直接生成cdn方式:Hbuilder X直接创建vue项目普通模式

第二类引入方式

把vue的代码环境集成后,在vue的代码环境中写项目,最终才经过webpack打包生成第一类方式
自己构建vue的脚手架:用npm下载vue引入到项目中然后webpack打包

	/*
1.新建项目  alipay 
2.初始化配置文件:npm init -y
3.下载依赖:
 npm i webpack@5.74.0 webpack-cli@4.10.0 vue-loader@17.0.0 @vue/compiler-sfc@3.2.37 webpack-dev-server@4.10.0  html-webpack-plugin@5.5.0 -D
npm i vue -S 
4.webpack.config.js配置:
*/
const path = require('path');
const {
	VueLoaderPlugin
} = require('vue-loader');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
	mode: 'production',
	watch: true,
	entry: './src/main.js',
	output: {
		filename: '[name].js',
		path: path.resolve(__dirname, 'dist'),
	},
	module: {
		rules: [{
			test: /\.vue$/,
			loader: 'vue-loader',
		}, ]
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: path.join(__dirname, 'src/index.html'),
			filename: 'index.html'
		}),
		new VueLoaderPlugin(),
	],
	devServer: {
		open: true,
		port: 8080,
		hot: true,
		host: '192.168.2.60',
		compress: true,
	},
}

//5.项目配置文件 pakage.json文件中:
//scripts:{
//"dev": "webpack serve --config webpack.config.js"
//}

//6.main.js文件:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
document.body.innerHTML+="444"

//7.模板html文件中
<body>
	<div id="app"></div>
<body>

使用官网脚手架的方式构建项目环境

1.  npm install  @vue/cli -g //下载官方脚手架
2.  vue create app1  //项目名称
3. 接下来让你选择一些默认要生成的工具,不管直接回车
4. 启动:
npm run serve  //生成的打包文件在内存中不会写入磁盘用于开发阶段
或者  
npm run build //生成的打包文件在dist中 用于项目上线

可视化项目管理方式: vue ui

	1.下载官网脚手架
	2.小黑窗命令 vue ui 可以通过 vue -h查看所有的命令
	3.在网页中创建项目	

编辑器直接生成环境
HbuilderX 创建项目 选择 vue项目 vue-cli默认模板
然后要:npm i
再:npm run serve

禁用eslint,在vue.config文件中

module.exports = {
    lintOnSave: false
}

Vue对象和基础指令

如何解决vue加载初的闪屏
1.添加一个属性 v-cloak vue运行时,会将v-cloak属性去掉,通过属性选择器隐藏标签,就没有括号闪烁了
2.利用$mount去挂载
3.使用指令v-text="" 底层xx.innerText = ""或者v-html="" => xx.innerHTML=""

v-pre 静默-跳过编译显示括号

普通插值表达式插入数据: 在标签尖括号中使用{{}}插入js表达式:变量,函数调用,三目运算等等。{{}}里放入data的属性名或者methods的方法名

body:
		<div id="app">{{msg}}</div>
js:
	new Vue({
		el:"#app",
		data:{
			msg:200
		}
	})

给元素绑定属性
给标签绑定vue对象中的变量
标准写法:v-bind:src = “变量”
简写: :src = “变量”

方法与事件绑定

methods:Vue中的方法都写在这里
通过给元素添加@click=“方法”,给元素绑定事件
元素可以同时添加多个事件-方法调方法

面试题:DOM操作中的监听器第三个参数true还是false能阻止冒泡?都不能阻止,默认是false只会影响元素是在捕获阶段触发还是冒泡阶段触发,false冒泡阶段触发,true捕获阶段触发

事件修饰符
@click.stop 阻止冒泡
事件修饰符,多修饰符可以多连 连点没有顺序
例如 .self.stop
.stop 阻止冒泡
.self 只有当目标是自己时才会触发,当冒泡到自己时不会触发,会跳过自己冒泡,继续冒泡下一个元素。不会影响事件链
.prevent 阻止元素的默认事件
.once 只绑定了一次,绑定了一次后就会事件解绑
样式绑定

直接在元素中绑定属性	这里需要注意里面的属性是驼峰命名
:style = "{display:'none',color:'red'}"
在对象中写属性,然后对元素绑定属性
<div :style="style">666</div>
	new Vue({
            el: "#app",
            data: {
                style: {
                    color: "red",
                    fontSize: "100px"
                }
            })	
绑定多个属性
<div :style=[style,{background:'red'}]>666</div>
	new Vue({
            el: "#app",
            data: {
                style: {
                    color: "red",
                    fontSize: "100px"
                }
            })	
class绑定
:class = "变量" //一个变量
:class = "['自己带的class',变量]" //多个class
:class = {class:true/false}	//true则有该class,false则没有该class

条件渲染

v-if:通过移除元素,true添加元素,false删除
具有更高的渲染消耗,常用在不常切换的业务

	<div v-if="true">
        {{msg}}
    </div>

v-show:通过css渲染消失,会占用内存
具有更高的内存消耗,常用在经常切换的业务

	<div v-show="true">
        {{msg}}
    </div>

循环渲染

v-for
v-for的元素和它的子元素都会被克隆

	<div id = ""app>
		<div v-for="el in arr">
			{{el}}
		</div>
	</div>

	new Vue({
		el:"#app",
		data:{
		 arr:["hello","vue","js"],
		}
	})

v-for与v-if连用
通过v-if的条件来限制v-for渲染
vue2.0中v-for与v-if写到一个元素中,v-for的优先级更高
1.把两者写成嵌套关系会多出来一个盒子,可以通过template标签
vue3.0不能写到一起 不然报错
v-for绑定key
key尽量绑独一无二的值
意义:多选框选择后加载更多的选择在前面出现选择的与内容不符合
1.提高绘制渲染效率
2.让数据跟真实的dom一一关联,让它不发生渲染混乱的情况
原理:

	 <div id="app">
        <button @click="add">点击</button>
        <div v-for="el in arr" :key="el.id">
            <input type="checkbox" name="goods">
            <span v-text="el.test"></span>
        </div>
    </div>

v-once:只渲染一次,后面数据源改变等都不会再修改这个

v-model:双向数据绑定

vue全家桶(vue.js vue-axios vue-router vuex element-ui)

什么是响应式数据
如果内存中的数据变化了,页面的UI也会动态跟着刷新,这种数据就是响应式数据
响应式数据设计原理
vue2.0 采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
缺陷: 对于数组来说劫持的是它的方法,通过数组的方法去修改数组的内容重新渲染页面,不能直接通过下标来进行修改。
vue3.0 使用了es6 proxy 代理了data对象
双向数据绑定
1.如果数据容器中的数据变了也会让页面刷新(dom操作让页面改变)
2.如果用户操作dom改变了页面,反之也会让数据容器中的数据的值改变

	原理:v-model实际是指定其:value和@input
	 <div id="app">
        <p>{{msg}}</p>
        <input type="text" :value="msg" @input="fn">
		<!-- v-model -->
		<input type="text" v-model="msg">

    </div>

        new Vue({
            el: "#app",
            data: {
                msg: "hello"
            },
            methods: {
                fn(e) {
                    this.msg = e.target.value;
                }
            }
        })

过滤器

filters:{}
{{msg|tool|tool2}}、{{msg|tool}}、{{msg|tool(arg1,arg2)}}
将msg作为参数传给tool过滤,再将这个结果传给tool2过滤
加小括号后会将前面的参数作为第一个参数,括号的参数依次传入
只能插值表达式({{}})和属性绑定(v-bind)使用

	<div id="app">
        <p>{{msg|tool}}</p>
        <a :href="url|tool2">百度</a>
    </div>

	 new Vue({
            el: "#app",
            data: {
                msg: "2001-7-26"
            },
            filters: {
                tool(str) {
                    let age = new Date().getFullYear() - new Date(str).getFullYear()
                    return age + '岁'
                }
				tool2(str) {
                    return "http://" + str
                }
            }
        })

计算属性

计算属性就是处理数据源中的数据,然后用于渲染,而且会监听计算属性中使用的数据源,然后把计算的结果缓存起来
如果监听的数据源发生了变化才会重新计算,否则直接使用缓存的数据渲染
缺点:如果简单的运算也用计算属性,反而会增加资源消耗(计算属性会去监听计算的值,而且会缓存计算的结果),比如生日转年龄时,可以用过滤器
	 <div id="app">
        <p>{{birth}}--{{age}}</p>
		        <p>{{x}}</p>
    </div>
	 new Vue({
            el: "#app",
            data: {
                birth: "2001-09-10"
            },
            computed: {
               // 语法糖
                age() {
                    return new Date().getFullYear() - new Date(this.birth).getFullYear()
                },
                // 标准写法
                x: {
                    get() {
                        // 取值
                        return 100
                    },
                    set(data) {
                        // 存值
                    }
                }
            }
        })
		//当data中其他的数据改变时,computed里的计算属性不会重新调用,computed会监听计算的数据,只有当计算的数据发生改变时,才会重新调用

属性侦听器

必须和data中的数据源同名
如果是侦听一个对象,如果修改对象内部的属性不会触发侦听器,只有修改了引用数据的引用,才能触发侦听器属性

深度侦听:侦听对象
侦听对象,如果对象内部属性发生改变,触发侦听器

	 new Vue({
            el: "#app",
            data: {
                obj: {
					n:100
				},
            },
            watch: {
                obj(){
				deep:"true",
				handler:()=>{}
	},
            },
        })

计算属性,属性侦听器,方法,过滤器有什么区别?那些是被this对象劫持过的?

计算属性-方法
	计算属性:会把计算的结果缓存起来,并监听计算过的数据源,如果监听的数据源发生变化才会重新计算(没有监听的属性发生改变,模板会重新渲染)
	方法:一般是给模板事件和其他方法使用,如果在模板中使用了方法,而且不是事件,如果模板中的数据源的任意数据改变了,它会重新调用
	过滤器:往往用于数据渲染前的数据处理,除了用法跟方法不一样其他都一样,它没有被劫持
	属性侦听器:只有侦听的属性发生变化才会触发(可以深度侦听,但是更消耗内存)

计算属性和方法被劫持了

自定义指令

v-name 自己定义的属性
vue 通过directives选项去接收
inserted:被绑定的元素进行DOM操作,当插入父组件时调用
	<div id="app">
        <p>{{n}}</p>
        <input type="text" v-focus>
    </div>

	 new Vue({
            el: "#app",
            data: {
                n: 100,
            },
            directives: {
                focus: {
					//指令的定义
                    inserted(el) {
						//指令的作用
                        el.focus()
                    }
                },
            }
        })

自定义指令钩子
bind:只调用一次,指令第一次绑定时调用,进行初始化设置
inserted(el,binding):当插入父组件时调用,el表示绑定的元素,binding表示传入的对象就是绑定的数据通过binding.value取出来
update:当前组件的数据源更新后才会调用
componentUpdated:
unbind:只调用一次,与元素解绑

Vue.nextTick
在vue中引入ECharts时,需要考虑引入时是否能操作vue同时页面标签的宽高是否加载完毕,
如果ECharts的配置项(options)在vue的data中会找不到options由于inserted在生命周期created之前先调用
此时通过Vue.nextTick(fn) 在延迟回调函数将ECharts延迟调用则可以直接显示在页面中

生命周期函数

在vue实例中达到一定条件后执行的函数,就将这些函数称为周期函数
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroy
	<div id="app">
        <p>{{msg}}</p>
        <button @click="change">修改数据源</button>
    </div>
	 new Vue({
            el: "#app",
            data: {
                msg: 6666
            },
            methods: {
                change() {
                    this.msg = 88888
                }
            },
            beforeCreate() {
                alert("此时我还没有创建vue对象,还不能对实例对象的data操作");
                // 这里还不能对vue进行操作,一般在这里进行预加载,不在这里进行对页面渲染的操作
            },
            created() {
                alert("此时我已经创建好了vue对象,已经可以对实例对象中的data进行操作了,但是还没有对页面进行渲染");
                console.log(this.msg);
            },
            beforeMount() {
                alert("此时我已经创建好了vue对象,准备对页面进行挂载(渲染)了");
            },
            mounted() {
                alert("此时我已经创建好了vue对象,已经完成了对页面进行挂载(渲染)了");
				console.log(this.$el)//当前组件的真是DOM
            },
            beforeUpdate() {
                console.log(this.msg);
                // 第一次渲染时不会执行这个钩子函数
                alert("此时数据源发生了改变,我要准备重新渲染页面了");
                // 只有当渲染的数据源发生了改变后才会进行这里的操作,没有渲染的数据源改变后不会执行这里的操作
            },
            updated() {
                alert("我已经重新渲染完成了");
            },
            beforeDestroy() {
                alert("这个实例对象即将毁灭,将这个页面中需要保存的数据缓存起来");
            },
            destroyed() {
                alert("啊!我死了")
            },
        })

组件

通过vue的components选项放组件

注册组件时,组件名不要用系统已经使用过的,组件template模板中,只能有一个根元素
组件名用驼峰命名时,使用时可以用连字符使用

一个组件中的data必须是一个函数,一个组件可以进行复用

全局:所有组件共同的功能(指令,过滤器,组件)
	Vue.filter("name",function)
	Vue.directive("name",function)
	Vue.component("tagName",option)
局部:只有当前组件的功能(指令,过滤器,组件)
	filters:{},
	components:{},
	directives:{}
注册组件时,使用时可以用双标签也可以用单标签:如果有插槽必须用双标签

单文件组件

在项目下创建vue.config.js就是vue的打包配置文件
@代表了src目录的文件,这里是vue的脚手架集成的basepath会去src文件夹里寻找,其他框架没有@
组件名不能跟vue中和原生DOM中的名字相同
如果.vue的script注释了,在打包的时候vue的打包环境会帮我们把这个文件解析为一个对象,然后给这个对象添加一个template属性,值为我们的<template>页面模板
<style>可以写多个
每一个组件内部只能有一个根元素,不要在根元素上写v-for

props
给子组件自定义一些属性,这个属性变成了子组件实例的属性
通过props将数据作为一个自定义的属性传递给子组件
属性的类型验证配置的方式注册组件的属性

	//直接注册组件的属性
	//props:['title','msg']
	//配置的方式注册组件的属性
	//这里配置的都是属性,通过给属性赋值传递给子组件
	props:{
		title:String,//这里会属性类型验证,字符串
		count:[Number,String],//这里会属性类型验证数字或者字符串
		color1:{
			default:"red",
			type:String,
		},
		pro1:{
			validator:function(v){//自定义验证
				let arr = ["success","danger"]//传入的值是否是数组中的值是就通过,否则就警告
				return arr.includes(v)
			}
		}
	}

组件中资源打包问题

项目中的本地资源打包时,webpack打包时遇到了import或者是标签内部直接写的路径时,都会启用file-loader等去加载资源,如果把图片等本地资源的路径写在data中,然后页面使用,因为webpack不会读取data所有不会打包资源

CSS作用域

全局样式
全局样式:.vue中的

样式穿透 4种方式

对环境有要求 /deep/ :deep >>> ::v-deep

在父组件的样式中写子组件的样式,可以避免选择器相同时子组件的样式被父组件的样式覆盖
	/deep/ .el{}
	parent	:deep .el{}
	parent:deep(.el){}
插槽 slot
<slot></slot>	//放在子组件中,表示后面这里是插槽的槽位(会在这里插入内容)
插槽内可以包含任何模板代码,包括HTML也可以包含其他组件
插槽可以通过name名指定是哪个插槽进行渲染
如果子组件中有多个插槽都没有分配name属性,并且父组件中使用时也没有规定name则会将所有没有name属性的插槽都插入
	//父组件
	<template>
		<div class="">
			<Box :title="msg">
				<div class="slot">
					{{title}}
				</div>
				<p name:s1>hello66666666</p>
				<p #s1>hello9999</p> //#s1是语法糖 标准写法v-slot:s1
				<!-- 
				* <template v-slot:s1>	//将template直接传进去
					<p>hello9999</p>
				</template> 
				* -->
			</Box>
			<p>hello66666666</p>
		</div>
	</template>
	<script>
		import Box from '@/components/Box.vue'
		export default {
			data() {
				return {
					msg: "这是父组件的传入给子组件的数据",
					title: "父组件的插槽"
				}
			},
			components: {
				Box
			}
		}
	</script>
//	===================================================
	//子组件:
	<template>
		<div class="box">
			<slot></slot>
			<!-- 如果父组件没有使用name属性则这里也会插入 -->
			<p>{{title}}</p>
			<slot></slot>
			<!-- 如果父组件没有使用name属性则这里也会插入 -->
			<slot name ='s1'></slot>
			<!-- 这里只会插入name:s1或name='#s1'的内容 -->
		</div>
	</template>
	<script>
		export default {
			props:['title']
		}
	</script>
自定义事件
v-on:xxx='fn',
v-on:click.native	表示是原生的事件,只有官方提供的事件才能加.native,自己写的加了没有作用

自己定义事件类型,触发条件由自己设计
触发组件内的事件
this.$emit("type",args)//第一个参数表示事件类型,第二个参数表示传入的值
当父组件调用子组件时,如果给父组件中给子组件绑定了官方事件,如果没有加native则不会使用,只有加了native后才会调用
	//父组件:
	<div>
	<box1 v-on:click.native="fn"></box1>	//这里如果不加native会被认为是自定义组件,必须在子组件内调用才会生效
	</div>
	
	//父组件
	<div>
	<box1 v-on:myclick="fn"></box1>	
	</div>
	//子组件
	<template>
		<div class="box">
			<button @click="x">点击一下</button> //子组件设定触发条件
			<p>这里也会跟着触发?</p>//不会
		</div>
		x(){
			this.$emit("myclick") //触发父组件给子组件绑定的事件
		}
	</template>

配置代理:webpack-devServer 笔记中

vue.config.js中配置
	proxy: {
	     '/api': {
	       target: 'http://192.168.10.10:7001',
      secure:true,//如果代理的target是https接口,需要配置它 
	       pathRewrite: { 
			   '^/api': '/' 	//这里会把请求的路径中含有/api的转为/
			   //例如请求:http://192.168.xx:8080/api/test/
			   //会变成:http://192.168.xx:8080/test/
			   },
			   //请求时重写pathname
			   //项目组件中请求的是http://192.168.10.10:8080/api/test
			   //8080服务器就会帮我们代理请求http://192.168.10.10:7001/test
	    },
	    },
baseURL
axios.defaults.baseURL='http://192.168.xx.xx:8080/api'
//配置公共的url 如果用这个axios去请求 '/ajax1',实际请求的是'http://192.168.xx.xx:8080/api/ajax1'
		//main.js:
		import axios from 'axios';
		axios.defaults.baseURL='http://192.168.xx.xx:8080/api'//这里一般在项目打包后会注释掉
		//前端页面请求axios('/test') 会自动拼接成http://192.168.xx.xx:8080/api/test
		Vue.prototype.$axios = axios	//将axios定义到全局去,每个组件都可以使用axios而不需要再次引入
组件中的data为什么设计成函数
1.组件复用时,使每个组件复用是独立的数据源,才不会让data数据源共用
2.函数的设计就像懒加载一样,当使用组件时,数据源的对象才会创建,这样设计性能更好
数据劫持的顺序
this组件对象有很多属性和方法都是劫持"别人"的:比如data、methods、props
this在构建时,是劫持了data>props属性>方法>计算属性>事件中给this添加的,使用的优先级:data<props属性<方法<计算属性<事件中给this添加的
初始化顺序 computed,methods,data,props
Sass、Less
vue create init时选择帮我们配置sass的加载器
需要下载一个sass预编译,在style标签添加lang="scss"就可以在标签里写sass代码了
		<style lang='scss'></style>
单项数据流
数据从顶层流到底层,只能从父组件传给子组件,不能反向传值就叫做单向数据流
vue加载流程
1.每一个组件在加载时都会调用vue内部的render函数来把这个组件的tamplate选项的模板解析为一个JS对象

这个对象跟DOM节点对象"长得一模一样",就是为了后来的渲染
2.然后是数据劫持代理监听等等
底层有一种设计: 发布/订阅设计 其实就是写了一个watcher函数去订阅(监听) 数据的改变(底层js语法就是defInedproty,v3是proxy)
当数据发生变化以后:
当状态(数据)变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异(同层比较从树的两边往中间比较,再比较下层)
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了

DIFF算法
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中

当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了

组件传值

父传子
通过给子组件传属性就将数据传进去了

	//父组件:
	<div>
		<p>父组件的msg——{{msg}} </p>
		<Box :msg=msg></Box>
	</div>
	
	data() {
		return {
			msg: "父组件的data",
		}
	},
	//子组件:
	<div class="box">
		<p>传给子组件的data——{{msg}}</p>
	</div>
	{
	props: ['msg'] //父组件传入的属性,
	}

子传父
通过自定义事件绑定后调用this.$emit(“type”,‘value’),将修改的数据传给父组件,父组件通过自定义事件修改数据

	//父组件:
	<div>
		<p>父组件的msg——{{msg}} </p>
		<Box @myclick="change" :msg=msg></Box>
	</div>
	
	data() {
		return {
			msg: "父组件的data",
		}
	},
	methods: {
		change(v) {
			this.msg = "88888"	//自定义事件触发后通过父组件修改data数据
		},
	}
	//子组件:
	<div class="box">
		<p>传给子组件的data——{{msg}}</p>
		<button @click="change">修改父组件data</button>
	</div>
	
	props: ['msg'],
	methods: {
		change() {
			this.$emit("myclick", this.msg)	//这里调用自定义事件去修改父组件的data,
		},
		
	}
	

xx.sync:将属性和事件绑在一起,子组件通过设置触发条件去修改父组件传入的数据的值,this.$emit(“update:a1”,“修改的内容”)

//	父组件:
	<test :my.sync='msg'></test>	//给子组件绑定自定义事件my,并传入属性my
	
//	子组件:
	<p>传入的数据——{{my}}</p>
	<button @click='fn'>修改数据</button>

	props:['my']	//接收传入的属性(数据)
	methods: {
		fn() {
			this.$emit("update:my", "子组件修改了数据");
		}
	}
组件间的双向数据绑定 v-model
组件通过v-model传值可以理解为:v-model也是:value,@input的一个语法糖
	父组件
	 <Box :value="msg" @input="某个函数"></Box> 
	子组件
	<button @click="change">chaneg1</button>

	props:["value"],
	methods:{
		change(){
				this.$emit("input","修改的内容")	
			}
	}
给子组件传入了value属性并且定义了input事件,因此子组件需要接收value属性,并写触发input事件的条件
v-model:属性名 = ''	可以自定义传入的属性名,不一定是value
v-model.number
v-model.trim
v-model.lazy:将input事件改为change事件

实际用法

	//父组件:
	<box1 v-model="msg"></box1>
	
	data(){
		return {msg:"父组件的数据"}
	}
===================================================================
	//子组件
	<p>{{value}}</p>
	<button @click="fn">change</button>
	
	props: ['value'],
	methods: {
		fn() {
			this.$emit("input", "子组件修改的数据")
		}
	}
组件多层传值
中间组件在给子组件传父组件的数据时,通过v-bind="$attrs"和v-on="$listeners",将父组件的数据全部传给子组件

中间组件不能用props去接受任何数据,否则不能传给中间组件的子组件,如果需要使用数据可以直接通过$attr.xx调用父组件传下来的数据,孙组件通过props去接收需要的数据

	//根组件:
	<p>父组件的msg——{{msg}} </p>
	 <Box :d1="msg" @x="xchange"></Box>
	data() {
			return {
				msg: "app组件的数据"
			}
		},
		methods:{
			xchange(v){
				this.msg = v;
			}
		}
		
	//子组件 中间组件
	<P>中间组件子组件——{{$attrs.d1}}</P>	//中间组件不能通过props去接收父组件传入的数据,因为要把数据传入给子组件
	<box1 v-bind="$attrs" v-on="$listeners"></box1>	//这个是中间组件引入的子组件
	components: {
			box1,
		},
		
	//孙组件
	<p>孙组件——{{d1}}</p>
	<button @click="fn">孙组件</button>
	
	props: ['d1'],	//传入的属性d1
	methods: {
		fn() {
			this.$emit("x", "孙组件修改的数据")	//满足条件后调用事件
		}
	}
获取组件 r o o t − root- rootparent- c h i l d r e n − children- childrenrefs
	$root: this.$root 会返回组件的根组件
	$parent: this.$parent 会返回组件的父组件
	$children: this.$children 返回组件的子组件对象数组,是乱序的数组,不是按照代码顺序的子组件顺序,与$refs一起用可以去操作子组件
	$refs:是一个对象,属性名就是绑定在模板中的ref的值,通过组件的'id'去访问组件,组件需要添加 ref = "xxx" 类似id名一样,访问时this.$refs.xxx就可以访问到该组件了,
	
	中间组件:
	<box1 ref="mybox1"></box1> //子组件
	<box1 ref="mybox" ></box1>  //子组件
	<button @click=fn>子组件</button>
	
	methdos:{
		fn(){
			console.log(this.$children,this.$refs.mybox,this.$parent,this.$root)
			// 子组件们(是一个乱序的数组对象)、id为mybox的子组件、组件的父组件、组件的根组件
		}
	}
依赖注入-Provide/Inject
提供者/消费者 
provide:{
	msg:'xxxxx'
}
provide(){
	return {msg:'xxxxx'}
}
inject:["msg"]

provide-inject 父组件给子组件的值是基本数据类型,就不会是响应式的:
1.provide写成一个函数返回一个对象,将数据内存空间引用传给子组件
inject接收
2.提供基本数据,子组件接收基本数据不会刷新页面(非响应式),父组件传入一个函数这个函数需要返回这个基本数据,子组件接收后,通过计算属性去计算调用这个函数,这样当父组件修改了这个数据后,子组件也会跟着变化
	根组件
	data(){
		return { obj:{msg:"父元素传值"}}
	}
	provide(){
		return {
			msg:(arg=this.msg) => {	//根组件传函数通过函数修改数据源
					this.msg = arg;//多层组件将值传进来修改数据
					return this.msg;
					}
				}
	}
	子组件
	inject:["msg"],
	provide() {
		return {
			n: this.obj	//父组件直接传入对象,同一个引用直接修改数据源
		}
	}
	孙组件
	<p>{{msg()}}---{{n.age}}</p>
	<button @click='fn'>孙组件修改</button>
	
	inject:["msg",'n'],
	methods: {
		fn() {
			console.log(this.msg("孙组件修改了内容"));//这里将修改的内容传给根组件的函数去修改数据源
			this.n.age = 60//可以直接修改父组件的数据源
		}
	}
中央事件总线 $bus

Vue提供的技术:某继承Vue的组件有三个功能
1.触发x组件的a事件:x. e m i t ( " a 事件 " , 参数 . . . ) 2. 给 x 组件绑定 a 事件 : x . emit("a事件",参数...) 2.给x组件绑定a事件:x. emit("a事件",参数...)2.x组件绑定a事件:x.on(“a事件”,监听器函数)
3.给x组件解绑a事件:x.$off(“a事件”,监听器函数)

	<button @click="fn1">绑定事件</button>
	<button @click="fn2">触发事件</button>
	
	methods: {
		fn1() {
			this.$on('look', function() {
				console.log(this);
			}) //绑定事件
		},
		fn2() {
			this.$emit('look');//触发事件
			this.$off('look')//解绑
		},
	}
动态组件——常用来切换组件
<component is="组件名"></component>	//传组件名替我们写组件,必须要先在components里注册组件
	<component :is="mytemp"></component>
	// v-bind:is = 'xxx' ==> :is = 'xxx'
	
	<button @click="fn('box2')"></button>
	//<button @click="mytemp='box2'"></button>
	data(){
		return {
			mytemp:"box1"
		}
	},
	methods:{
		fn(name){
			this.mytemp = name;
			//进行组件间的切换,切换组件时,之前的组件就被销毁了
		}
	},
	beforeDestroy:{
		//这里组件销毁时,将用户输入的数据保存下来
	}
缓存组件 keep-alive
将组件放在<keep-alive>标签里面,就可以将组件缓,即缓存用户的操作例如用户输入的信息等等
	<keep-alive include = 'box1' exinclude = 'box2' :max='2'>
		<component :is="mytemp"></component>
	</keep-alive>
	//默认全部加载过的组件都会缓存起来
	//该标签属性:
	// include 需要缓存的组件 可以写组件名字符串也可以写正则,
	//	:include  = "/box1|box2/"
	// exinclude 不需要缓存的组件
	// max 数字最多缓存几个,缓存最近的被渲染过的几个组件

keep-alive提供的钩子函数:写在被切换的组件内部
activated:切回该组件的时候才会执行该函数
deactivated:切其他组件的时候才会执行该函数,从一个组件切到另一个组件的时候

异步组件——懒加载组件
需要这个组件时才显示,常与动态组件连用
	component:{
		Box:() => ({
  // 需要加载的组件 (这个 `import` 函数会返回一个 `Promise` 对象。)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
}),
}

路由

路由配置
npm i vue-router 自己下载
vue creat xxx 用脚手架创建时,选上vue-router
router/index.js:
import Vue from 'vue'
//引入路由插件
import VueRouter from 'vue-router'
//引入首页组件:一开始就会打包,因为首页一般是用户直接访问的页面
import Home from '../views/home/index.vue'
//给组件的原型链绑定功能:this.$router this.$route
Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/login/index.vue')
  }
]

//配置规则
const router = new VueRouter({
  mode: 'history',
  routes
})
export default router

main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
	//引入路由:主要功能就是配置路由的规则(路由模式	注册的路由地址-网址	组件原型链中绑定路由对象和路由信息)

new Vue({
  router,
	 //挂载路由到整个项目中:主要功能就是监听地址栏的改变,然后修改项目中的router-view组件应该加载的组件
  render: h => h(App)
}).$mount('#app')

页面跳转

router-link

	<a href="/login">go login</a><br>
	//a标签会有请求服务器,然后刷新网页,因此用在链接外部网站
	<router-link to={path:'/login'}>go info</router-link>
	<router-link to='/login'>go info</router-link>
	//它渲染到页面也是a标签但是只是改变了地址栏的网页并没有重新加载网页
	//路由的底层就是采用history去监听地址栏的变化,然后把当前app组件中的router-view切换了,这种跳转用在网站内部的路由跳转
	<button @click='goinfo'>js跳转</button>
	methods:{
		goinfo(){
			this.$router.push({
				path:'/login',
			})
		}
	}
跳转路由传参
传递的参数都在路由信息对象中:路由对应的组件中获取 this.$route
1.直接在路由中拼接传的数据
	<router-link to='/login?id=123&age=23'>go info</router-link>
2.通过methods方法中的跳转时,通过this.$router{path:'xxx',query:{name:'xx',age:'21'}}

接收数据
目标网址在created后通过this. r o u t e . q u e r y 获取数据 , 如果没有传参就是 u n d e f i n e d ∗ ∗ 动态传参 ∗ ∗ m e t h o d s 方法中传入参数 , 将参数传给 q u e r y , 或者拼接到网址中 t h i s . route.query 获取数据,如果没有传参就是undefined **动态传参** methods方法中传入参数,将参数传给query,或者拼接到网址中 this. route.query获取数据,如果没有传参就是undefined动态传参methods方法中传入参数,将参数传给query,或者拼接到网址中this.router.push(/login?id=${arg}$age=21)

动态路由传参(params传参)
	js:
	{
	  path: '/login/:id',
	  name: 'login',//用于路由跳转
	  component: () => import('../views/login/index.vue')
	}
	
	<router-link to='{name:'注册的路由的name',params:{id:参数}'>go info</router-link>
	this.$router.push('/login/参数')
	this.$router.push({name:'login',params:{id:参数})//必须用name跳路由
路由嵌套
添加一个children属性,存放子路由信息,path中可以不添加/如果添加/的话需要加上前面父组件的路径例如:/login/xx =>login下的xx路由
	const routes = [
	  {
	    path: '/',
	    name: 'home',
	    component: Home
	  },
	  {
	    path: '/login',
	    name: 'login',
	    component: () => import('../views/login/index.vue'),
		children:[
			{path:'a',name:"a",component:()=>import('xxx.vue')},{path:'b',name:"b",component:()=>import('xxx.vue')},{path:'/login/b',name:"b",component:()=>import('xxx.vue')}
		]
	  }
	]
通配路由
	const routes = [
		...,
		...,
		{
			path:'/xx', 
			component:()=>import('xxx.vue'),
			redirect:'/xx/xx', //用户输入的是/xx路由,帮他重定向到/xx/xx
			children:[
				{
				path:'*',//如果输入的是/xx/随便乱输,会重定向到/xx/xxx
				component:()=>import('xxx.vue'),//乱输跳的页面
			}
			]
			
		}
		,{
			path:'/*', //网址随便乱输,一般用来404网页
			component:()=>import('xxx.vue')
		}
		
	]
路由前进后退
mode:"history"	
底层选择切换组件的方式
history:底层是H5的window.history的技术,当地址栏的history状态发生变化时,切换了router-view渲染的组件,来'欺骗'用户达到切换新网页的效果,需要后端配合
hash:底层是老的技术Hash,当地址栏的Hash状态发生变化时,切换了router-view渲染的组件,来'欺骗'用户达到切换新网页的效果,hash值是不会发送给后端的,使用不需要后端配合
抽象模式:
SPA singe page app 单页应用
动态注册路由
//添加路由,按照顺序给路由末尾添加一个新路由,如果之前的路由配置里有*路由则添加的路由都不会找到,
router.addRoute({
	path: "/test",
	component: () => import('../test/index.vue')
})
//给name名为home的路由添加一个子路由,需要在home中添加一个router-view标签
router.addRoute('home', {
	path: "/test",
	component: () => import('../test/index.vue')
})

路由守卫

全局守卫router.beforeEach(function(){}):导航触发-一般用于登录验证
无论进入那个路由只要匹配一次路由,都要先调用这个函数——鉴权一般都写在这里

	router.beforeEach(function(to,from,next){
		//to:路由信息对象,目标路由
		//from:路由信息对象,原路由(从哪里跳的),如果是直接加载网址from为空
		//next():去匹配路由然后加载组件
	if(to.path == '/car'){
		next('/login') //去登录页面
	}else{
		next()
	}
		
	})

全局守卫router.beforeResolve(function(){}):
路由匹配完毕但还没有渲染会调用这个函数

	router.beforeResolve(function(to,from,next){
		next() //渲染组件
	})

全局守卫router.afterEach(function(){})
组件已经渲染完毕后调用这个函数,一般用于操作window,例如切换组件把滚动条滚到顶部

	router.afterEach(function(){
		window.scrollTo(0,0)
	})
	

路由守卫beforeEnter(function(){}):只对单个路由设置
写在路由对象内部

	beforeEnter(function(){
		next()
	})

组件生命周期守卫beforeRouteLeave(function(){})
组件要离开时,记录用户行为和一些计时器关闭等等操作

	beforeRouteLeave(function(){
		next()
	})

组件生命周期守卫beforeRouteEnter(function(){})
路由跳过来了,组件没有还没有激活(渲染),this用不了

	beforeRouteEnter(function(){
		next()
	})

组件生命周期守卫:beforeRouteUpdate(function(){})
组件重用时被调用----路由传参获取参数,避免增添watch开销
导航守卫执行顺序:beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < beforeRouteEnter < beforeResolve < afterEach

vuex仓库

特点:
能够在vuex中集中管理共享的数据,易于开发和后期维护
能够高效地实现组件之间的数据共享, 提高开发效率
存储在 vuex中的数据都是响应式的,能够实时保持数据与页面的同步
一般情况下,只有组件之间共享的数据,才有必要存储到vuex中;
对于组件中的私有数据,依旧存储在组件自身的data中即可

安装
	npm i vuex --save
	
	导入:
	import Vuex from 'vuex'
	Vue.use(Vuex)

vuex的使用

		//main.js需要引入
		import store from './store'
		//store/index.js
		Vue.use(Vuex);
const sotre = new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

State
创建state状态,状态就是那个存数据的对象,类似于vue中的data用于存放数据
	const store=new Vuex.store({
        state:{msg:"仓库所有共享的数据"}
})
组件中访问数据-通过this.$store.state.xxx取出数据
	this.$store.state.msg
	//修改数据
	this.$store.state.msg = '修改了数据' //官网不建议这样直接修改

Getter ——通过方法获取state的数据
	const store=new Vuex.store({
	  state: {
      	name:"Tom"
  },
  getters: {
      total(state){
        return state.name 
      }
	})
	//使用:
	this.$store.getters.total //得到state中的name数据
mutation

组件中希望更改 Vuex 的 store 中的状态(数据)的唯一方法是提交 mutation
不要用赋值表达式直接在组件中给store设置新数据
这样设计的原因是,只有通过mutation来更新数据,它才会去帮我们通知所有使用数据的组件更新数据 刷新UI

	const store = new Vuex.Store({
  state: {
      msg:66666,
      name:"Tom",
      obj:{
        name:"jack",
        age:18
      }
  },
  getters: {
      total(state){
        return state.name
      }
  },
  mutations: {
      changeAge(state,){//第一个参数传入state
        state.obj.age++;
      }
  }
})
//组件中使用:
	this.$store.commit('changeAge',{n:100})//传入了一个对象
	
// 以一个对象的形式传入,传入对象时,相当于把整个对象作为了第二个参数传入mutations
	this.$store.commit({
	type: 'changeAge', //调用的方法
	n:100 //传入了一个数据
})

mutations中修改仓库数据,只能在同步代码中实现,不要在异步函数中实现,否则会出现不刷新页面的情况

Action

​ 如果我们想要异步的修改数据,就可以使用actions异步的提交mutations
​ 可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters
​ 1.直接分发

	//store/index.js
	
	 actions: {
      changeAge(context){	//默认第一个参数传一个跟store一样的对象
        context.commit('changeAge')
      }
  }
//组件中使用:
	this.$store.dispatch('changeAge',{n:100})

​ 2.以对象形式分发

	 actions: {
      changeAge(context){	//默认第一个参数传一个跟store一样的对象
        context.commit('changeAge')
      }
  }	
//组件中使用:
	this.$store.dispatch({
        type:'changeAge',
        n:100
           })
Module
可用于业务分块开发:

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

	//分块设计:单独创建一个js文件作为一个模块
const moduleA = {
  namespaced: true,//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)	
  state: { msg:1 },
  mutations: { change(state,n){state.msg=n} },
  actions: { change(context,n){context.commit("change",n)} },
  getters: { x(state){return state.msg} }
}

const moduleB = {
   namespaced: true,//局部命名空间	
   state: {  msg:1 },
   mutations: { change(state,n){state.msg=n} },
   actions: { change(context,n){context.commit("change",n)} },
   getters: { x(state){return state.msg} }
}

//在Vuex中添加modules属性
const store = new Vuex.Store({
  modules: {
    a: moduleA, //a代表使用时给模块的名字,moduleA是引入的模块
    b: moduleB
  }
})

//组件中的使用:
this.$store.state.a.msg // -> moduleA 的状态(数据)
this.$store.state.b.msg // -> moduleB 的状态(数据)
this.$store.commit("a/change",100)-> moduleA 的mutations
this.$store.commit("b/change",200)-> moduleB 的mutations
this.$store.getters["a/x"]-> moduleA 的getters
this.$store.getters["b/x"]-> moduleB 的getters
this.$store.dispatch("a/change",100)-> moduleA 的actions
this.$store.dispatch("b/change",200)-> moduleB 的actions

Element-ui

安装:npm i element-ui -S
完整引入:

	//main.js中
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});

按需引入:

	//main.js中
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';

//具体引入哪一个组件看官方api
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
 * Vue.use(Button)
 * Vue.use(Select)
 */

new Vue({
  el: '#app',
  render: h => h(App)
});

网站公告

今日签到

点亮在社区的每一天
去签到