目录
一、watch监听及深度监听
二、directive自定义指令详解+实例
三、1.vue父子组件:数据双向绑定
一、数据双向绑定.sync(支持多个双向绑定值)
三、2.父子组件间方法的调用
1.this.$parent
三、3.兄弟组件间传值
三、4.vue子组件调用多层父组件的方法1——递归
三、4.vue子组件调用多层父组件的方法2-桥梁
四、Message消息提示每次只弹出一个 + 设置全局message弹框的格式
问题描述
Message消息提示每次只弹出一个 + 设置全局message弹框的格式
Message距离窗口顶部的偏移量
五、什么是重定向
六、为什么要进行重定向?什么时候需要重定向?
七、HTTP协议详解之响应篇
八、export与export default区别
九、路由传参-使用encodeURI加密参数
十、base64格式的加密与解密
十一、vite
1.介绍
2.速度
3. 支持框架
4.Vite与webpack对比
5.Vite原理
6.总结
7.支持的浏览器
8.vite 中使用css预处理器等
9. vite 打包将 js 和 css 文件夹分离
10.vite 根目录vite.config.js配置文件读取env变量
11.vite 项目index.html中使用env环境变量
12.引用静态资源和 public 目录
13.打包生产环境移除 console 和 debugger
14.vue-router4.x路由 404 匹配
15.vite 启动后关于defineEmits的警告
16.vue3 和echarts5动态更新数据报错
17.Vue3.x 使用
18.vue3 中的 vue-router4.x 下的 keep-alive 写法
19.vue-router4 监听路由变化 onBeforeRouteUpdate
20. nginx 线上部署,刷新后页面 404
21. 参考资料
十二、单文件组件
1.动态组件:is
2.顶层的绑定会被暴露给模板
3.使用组件
4.组件传值(defineProps 、defineEmits)
1.使用ts
2.非ts
5.provide和inject
浅析
6.computed函数-计算属性与监视
7.watch函数
十三、ref,toRef,toRefs,reactive,onMounted的区别与用法
ref vs toRef
toRef vs toRefs
ref vs reactive
十四、快速生成vue3代码模板
1、如何创建模板
2、如何使用模板
十五、命令升级
十六、mixins
1.mixins的使用方法和注意点
mixins基础概况
一、定义公共的mixins文件:如mixin.vue
二、在你页面内调用:需要import这个mixins文件 ,然后通过mixins:['文件名']来使用就可以了
三、下面来说说mixins的特点:
十七、Object.defineProperty方法(详解)-Vue数据双向绑定原理
1.说明:
2.当是数据属性时:
3.当是访问器属性时:
4.定义多个属性
十八、插入排序
十九、a++和++a的区别
watch:{
//最初绑定的时候是不会执行的
a(val, oldVal){//普通的watch监听
console.log("a: "+val, oldVal);
},
//最初绑定的时候就执行
b:{
//深度监听,可监听到对象、数组的变化
handler(val, oldVal){
console.log("b.c: "+val.c, oldVal.c);
},
//代表在wacth里声明了b这个方法之后立即先去执行handler方法
immediate: true,
//deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器
deep:true, //true 深度监听
}
}
注意对象的写法奥~
watch: {
'node.indeterminate'(val) {
this.handleSelectChange(this.node.checked, val);
},
'node.checked'(val) {
this.handleSelectChange(val, this.node.indeterminate);
},
'node.expanded'(val) {
this.$nextTick(() => this.expanded = val);
if (val) {
this.childNodeRendered = true;
}
}
},
说明
除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
实例1
拖拽:
Drag.js:
export default function(el){
let oDiv=el;
oDiv.onmousedown=function(e){
let l=e.clientX-oDiv.offsetLeft;
let t=e.clientY-oDiv.offsetTop;
document.onmousemove=function(e){
oDiv.style.left=e.clientX-l+'px';
oDiv.style.top=e.clientY-t+'px';
};
oDiv.onmouseup=function(){
document.onmousemove=null;
oDiv.onmouseup=null;
}
}
}
Vue引用:
我可以拖拽
import drag from 'drag.js'
Vue.directive('drag',drag)
实例2
当页面加载时,该元素将获得焦点 (注意:autofocus
在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus
property,如下:
大致说明:
例:
父组件
子组件
说明:
- 在子组件中想要向父组件传值,我们通常使用
this.$emit()
方法- 在子组件中想要调用到父组件的方法和属性,使用
this.$parent
,例如:
// 在子组件中调用父组件的method1方法
this.$parent.method1()
// 获取父组件属性值
this.$parent.prop
注意:
- 这个方法有个前提条件: 父组件在应用子组件的时候,位置不能随意放,例如不能放在element UI组件的插槽里, 通常要放在根元素div里
一、$emit $on
// 建立一个空的Vue实例,将通信事件挂载在该实例上
// emptyVue.js
import Vue from 'vue'
export default new Vue()
// 兄弟组件a:childa.vue
A组件->{{msg}}
// 兄弟组件b:childb.vue
b组件,a传的的数据为->{{msg}}
// 父组件:parent.vue
父子组件传值多层的传值,provide和inject。
注意奥:
父组件:
export default {
provide() {
return {
fatherMethod: this.popupOpen, // popupOpen是父级的一个事件名
};
},
methods: {
popupOpen(params) {
console.log(params)
}
}
}
子组件:
export default {
inject: ["fatherMethod"],
methods: {
init() {
this.fatherMethod(params); // params是可以选择传的参数
}
}
}
使用方法非常简单只需要在路过的组件上v-bind="$attrs" v-on="$listeners"
我们来三个组件来演示父组件向孙组件传数据,孙数据向父组件传数据。
1.孙组件
this.$emit("updategrand",this.msg)
2.父组件(起着一个桥梁的作用)
3.祖父组件
fm(arg){
this.msg=arg
}
- Element UI的Message消息提示是点击一次触发一次的,如果一直点,然后就会出现打开多个的情况。
- 在后台弹消息的时候,也会同时出现
- 设置全局message弹框的格式
- Message距离窗口顶部的偏移量
在main.js平级建立文件夹,其中新建message.js
message.js:
/**防止重复点击重复弹出message弹框 */
import {
Message
} from 'element-ui';
let messageInstance = null;
const resetMessage = (options) => {
if (messageInstance) {
messageInstance.close()
}
messageInstance = Message(options)
};
/**设置message弹框的格式 */
['error', 'success', 'info', 'warning'].forEach((item) => {
resetMessage[item] = (msg) => {
return Message({
showClose: true,
duration:2000,
type: item,
message: msg
})
}
})
export const message = resetMessage
main.js:
import {message} from './request/message.js';
Vue.prototype.$message = message;
```
public.css:
```
.el-message {
top: 48%!important
}
```
注意
因为使用了new DonMessage()的原因,所以导致this.$message(options)的方式无法使用。所以推荐使用this.$message.success('成功提示')或者this.$message.success(options)的方式进行调用。具体参数可以查看官方文档。
就是地址A跳转到地址B啦。百度百科的解释:重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。
1)网站调整(如改变网页目录结构);
2)网页被移到一个新地址;
3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息–表示请求已接收,继续处理
2xx:成功–表示请求已被成功接收、理解、接受
3xx:重定向–要完成请求必须进行更进一步的操作
4xx:客户端错误–请求有语法错误或请求无法实现
5xx:服务器端错误–服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200 OK //客户端请求成功
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
eg:HTTP/1.1 200 OK (CRLF)
1.export default 和export都可以用于导出常量,函数,文件,模块等;
2.可以在模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用。
3.在一个文件或者模块中,export,import可以有多个,但是export default只能有一个。
4.通过export方式导出,在导入的时候需要加{},export default不需要在导入的时候加{}
使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。
在路由切换时页面需要使用地址栏传参,但地址栏会暴露参数的值,然后想到了encodeURI加密参数
比如参数是一个对象obj
obj:{
id: 1,
name: 'Tom'
}
那么需要将参数转换为JSON字符串,在使用encodeURI加密,需要注意的是通过路由跳转的时候会自动解密一次,所以需要加两次密。
let param = {
id: 1,
name: 'Tom'
}
param = encodeURI(JSON.stringify(param))
param = encodeURI(param) // 第二次加密
this.$router.push({path: `/record-index-city/${param}`,})
解密方式是使用decodeURI
let param = this.$route.params.param
param = JSON.parse(decodeURI(param))
加密后效果:
window.atob() 与window.btoa()可以实现对base64格式的数据进行解码和编码,其兼容性是主流浏览器,IE10及以上。
window.atob(encodedStr)只可以解码用btoa方法编码的字符串。
window.btoa():将ascii字符串或二进制数据转换成一个base64编码过的字符串,但该方法不能直接作用于Unicode字符串
当遇到中文时,需要先对中文转码否则会乱码。
var str = btoa(encodeURIComponent("中文汉字"));
//还可以解码回来
decodeURIComponent(atob(enc)) => 中文汉字
$ npm i vite @vitejs/plugin-vue --save-dev
Vite
使用了 原生 ES 模块,
期间没有涉及模块编译过程,节约了不少时间。
vite
支持的框架有 6 种
vanilla
:Vanilla JS 是一个快速、轻量级、跨平台的JavaScript框架。Vanilla JS 是世界上最轻量的JavaScript框架(没有之一) —— 其实这玩意就是原生 JS。vue/react
:不过多介绍。preact
:React 的轻量级替代方案。lit
:Lit 是一个简单的库,用于构建快速、轻量级的 Web 组件。(看了一眼语法,感觉还挺好玩的。)svelte
:一个不使用 Virtual DOM
的库 —— 真酷。这个库的作者和 Rollup
的作者是同一人。1、给一个入口文件,通过路口文件找依赖,然后就找到很多模块出来,模块中有js,css和图片总之包含了一大堆文件
2、然后webpack打包,通过loader,plugin等等进行打包;
3、打包完过后就变成非常少量的打包之后的文件,可能是一个也可能是多个,但肯定不是原始的模块文件;
4、打包完后,在开发阶段还要启动一个开发服务器,然后配置服务器,然后启动开发服务器,
5、最终通过访问开发服务器,他就会把打包后的结果给我们
这些过程在vue的开发过程运行项目时的打包其实是在内存中完成的,所以经过这一系列的方式会
使得在开发阶段每次修改代码都会运行打包,如果依赖太多运行就会很慢
1、vite在开发阶段没有打包过程,他是直接启动一个服务器,启动后就啥事也没有做
2、请求一个模块到开发服务器;
3、开发服务器编译模块,根据页面用所需要的依赖去加载文件
4、加载完成后,开发服务器把编译的结果返回给页面
这使得提高了我们在开发阶段运行的效率
通过修改配置最低支持到 es2015
,也就是 ES6。
处理成 es5:
// vite.config.js
import { defineConfig } from "vite";
// https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
import legacy from "@vitejs/plugin-legacy";
export default defineConfig({
plugins: [
legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
}),
],
build: {
target: "es2015", // 默认 "modules"
},
});
css
预处理器等vitejs.cn/guide/featu… 直接进行安装,不用像 webpack 那样安装 loader 和配置
npm install -D less
复制代码
复制代码
vite 中使用 postcss
vitejs.cn/config/#css… vite 中已集成了 postcss,无需再单独安装
vite 中使用 autoprefixer
import autoprefixer from "autoprefixer";
export default defineConfig({
css: {
postcss: {
plugins: [autoprefixer],
},
},
});
复制代码
默认打包后,js 和 css 是全部混在一起的,对强迫症很难受
github.com/vitejs/vite…
export default defineConfig({
build: {
rollupOptions: {
output: {
chunkFileNames: "static/js/[name]-[hash].js",
entryFileNames: "static/js/[name]-[hash].js",
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
},
},
},
});
复制代码
vite.config.js
配置文件读取env
变量在配置文件中直接使用import.meta.env.xxx
这样读取是报错的,不过有了上面的异步配置,读取env
变量就方便了,我们可以直接使用fs
直接获取
.env
文件# PROXY_URL
VITE_PROXY_URL=http://xxxxxx/
# DBPATH_ENV
DBPATH_ENV=XXXX
vite.config.js
文件const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const fsState = promisify(fs.stat);
const readFile = promisify(fs.readFile);
// 定义一个函数,读取.env文件中的内容
async function getEnvConfig(vite_env_key) {
const envFilePath = path.resolve(__dirname, "./.env");
const [notExistEnvFile, existEnvFile] = await fsState(envFilePath)
.then((data) => [null, data])
.catch((e) => [e, null]);
if (notExistEnvFile || existEnvFile.size <= 0) return;
const envContent = await readFile(envFilePath, "utf8");
if (!envContent) return;
const envContentArr = envContent
.split("\n")
.filter((str) => !str.startsWith("#")) // 过滤掉注释行
.filter(Boolean);
const resultKey = envContentArr.find((item) => item.includes(vite_env_key));
return resultKey ? resultKey.split("=")[1] : null;
}
const resolveConf = async () => {
// 读取 .env 文件中的VITE_PROXY_URL的值
const PROXY_URL = await getEnvConfig("VITE_PROXY_URL");
return {
server: {
host: "0.0.0.0",
port: 3000,
open: true,
proxy: {
"/api": {
target: PROXY_URL,
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
};
};
export default defineConfig(resolveConf());
index.html
中使用env
环境变量如何像webpack
项目的html-webpack-plugin
那样使用<%= htmlWebpackPlugin.options.title %>
,这种方式动态注入变量,vite 中可以使用vite-plugin-html
来完成。
npm i vite-plugin-html -D
复制代码
key
)vite.config.js
配置文件import { minifyHtml, injectHtml } from "vite-plugin-html";
const AMAP_KEY = "xxx";
export default defineConfig({
plugins: [
injectHtml({
data: {
title: "hello",
// 高德地图
injectAMapScript: ``,
},
}),
],
});
index.html
中使用 ejs 模板引擎动态注入
<%- title %>
<%- injectAMapScript %>
在 vue 文件中template
和style
中以相对路径和绝对路径两种方式引用静态资源
public
目录下的文件应使用绝对路径引用,例如public/icon.png
应该使用/icon.png。
public
中的资源不应该被JavaScript
文件引用。
export default defineConfig({
build: {
// 生产环境移除console
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
},
});
vue-router4.x
路由 404 匹配捕获所有路由或 404 Not found 路由
import { createRouter, createWebHistory } from "vue-router";
const Result = () => import("@/views/Result");
const router = createRouter({
history: createWebHistory(),
routes: [
{
// 注意这里,404页面匹配规则和以前不相同,要采用这种配置方式才行
path: "/:pathMatch(.*)*",
component: Result,
name: "Result",
},
],
});
export default router;
defineEmits
的警告
defineEmits
is a compiler macro and no longer needs to be importe
删掉defineEmits
的引用即可
- import { reactive, defineEmits } from 'vue';
+ import { reactive } from 'vue';
// 直接使用defineEmits
const emit = defineEmits(['test1', 'test2']);
emit('test1', 'hello1');
echarts5
动态更新数据报错markRaw
指定为非响应式即可
如何设置组件name
名称实可以写两个 script 标签,下面两个可以并存
之前这样写,会报警告
根据提示,得这样写:
onBeforeRouteUpdate
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
onBeforeRouteUpdate((to, from, next) => {
if (from.name === 'MachineList') {
...
}
next();
});
复制代码
修改 nginx 配置(服务器配置)
location / {
root /usr/share/nginx/dist; # 服务默认启动目录
index index.html index.htm; # 默认访问文件
+ try_files $uri /index.html; # 防止浏览器刷新后,页面404
client_max_body_size 100m;
}
由于组件被引用为变量而不是作为字符串键来注册的,在 中要使用动态组件的时候,就应该使用动态的
:is
来绑定:
当使用 的时候,任何在
声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用:
{{ msg }}
import 导入的内容也会以同样的方式暴露。意味着可以在模板表达式中直接使用导入的 helper 函数,并不需要通过 methods
选项来暴露它:
{{ capitalize('hello') }}
注意:子组件的方法要被父组件调用时,子组件的方法需要被暴露出来。
defineExpose({ addItemShow,addItemShow2 })//将子组件方法暴露出来,让父组件调用
范围里的值也能被直接作为自定义组件的标签名使用:
将 MyComponent
看做被一个变量所引用。如果你使用过 JSX,在这里的使用它的心智模型是一样的。其 kebab-case 格式的
同样能在模板中使用。不过,我们强烈建议使用 PascalCase 格式以保持一致性。同时也有助于区分原生的自定义元素。
defineProps
、defineEmits
)
script setup>
中必须使用 defineProps
和 defineEmits
API 来声明 props
和 emits
,它们具备完整的类型推断并且在 中是直接可用的
父组件
子组件
// 1.接收父组件传值
const props = defineProps<{
foo: string
bar?: number
}>()
// 2.1不设置默认值
interface Props {
msg?: string
labels?: string[]
}
// 2.2想要设置默认值,通过 withDefaults 编译器宏
const props = withDefaults(defineProps(), {
msg: 'hello',
labels: () => ['one', 'two']
})
console.log(props.msg) // hello
// 抛出事件
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
const click = () => {
emit('change', 1)
emit('update', 'abc')
}
//不设置默认值
const props = defineProps({
foo: String
})
//设置默认值
const props = defineProps({
name: {
type: String,
default: ""
}
});
const emit = defineEmits(['change', 'delete'])
provide
和inject
主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
定义说明:这对选项是一起使用的。以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
通俗的说就是:组件得引入层次过多,我们的子孙组件想要获取祖先组件得资源,那么怎么办呢,总不能一直取父级往上吧,而且这样代码结构容易混乱。这个就是这对选项要干的事情。
provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。
例子:
provide :父组件提供给子组件的数据或方法
inject:子组件获取父组件提供的数据或方法
let obj = reactive({
name: 'haha',
age: 18
})
let fullName = computed({
get () {
return obj.age + 1
},
set (value) {
obj.age = value
}
})
watch接收三个参数 监听的对象,监听的回调和监视的配置参数
watch('被监听的对象',()=>{},{immediate:'立即监听',deep:'深度监听'})
let age = ref(18)
let name = ref('小明')
let obj = reactive({
money: 100
})
//情况一:监视ref所定义的一个响应式数据
watch(age, (newValue, oldValue) => {
console.log('age', newValue, oldValue)
},{immediate:true})
//情况二:监视ref所定义的多个响应式数据
watch([age, name], (newValue, oldValue) => {
console.log('age-name', newValue, oldValue)//也是数组形式返回
})
// 情况三:监视reactive所定义的一个响应式数据的全部属性
watch(obj, (newValue, oldValue) => {
// 如果监听的是正规响应式对象的话
// 1.注意:此处无法正确的获取oldValue
// 2.注意:强制开启了深度监视(deep配置无效)
})
// 情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(() => obj.money, (newValue, oldValue) => {
// [()=>person.name,()=>person.age]多参数时候也需要数组
// 监听的参数需要以函数的形式返回才可以监听到
//当监听的参数是深层对象,需要配置deep为true
})
在setup()中可以用return { ...toRefs(object)}的方式,将整个响应式对象object的所有属性提供给外部使用。
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。例子:
VScode
编辑器中打开,【文件】–>【首选项】–>【用户片段】–>【新代码片段】–> 取名vue.json
确定{
// Place your snippets for vue here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Print to console": {
"prefix": "vue3",
"body": [
"",
" ",
"",
"",
"",
""
],
"description": "Log output to console"
}
}
vue
页面,在文章中输入vue3
后npm升级
npm install -g npm
混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
具体操作,so easy:
调用mixin方法
1、方法和参数在各组件中不共享
比如混入对象中有一个 cont:1 的变量,在组件A中改变cont值为5,这时候在组件B中获取这个值,拿到的还是1,还是混入对象里的初始值,数据不共享
2、值为对象的选项,如methods,components等,选项会被合并,键冲突的组件会覆盖混入对象的,比如混入对象里有个方法A,组件里也有方法A,这时候在组件里调用的话,执行的是组件里的A方法
3、值为函数的选项,如created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用,同一个钩子函数里,会先执行混入对象的东西,再执行本组件的。
- 首先Object.defineProperty是一个方法
- 这个方法接收三个参数: 1.属性所在的对象 2.属性的名字 3.一个描述符对象(数据属性;访问器属性)
- 数据属性:
1.configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
2.enumerable:表示能否通过for in循环访问属性,默认值为false
3.writable:表示能否修改属性的值。默认值为false。
4.value:包含这个属性的数据值。默认值为undefined。
访问器属性:
1.get:在读取属性时调用的函数,默认值是undefined 2..set:在写入属性的时候调用的函数,默认值是undefined
var p1 ={
name:"lisi",
}
//通过这个方法设置好configurable 这个属性,delete就不能把name属性给删除掉了。
Object.defineProperty(p1,"name",{
configurable : false,//表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
})
console.log(p1); //{ name: 'lisi' }
delete p1.name;
console.log(p1); //{ name: 'lisi' }
//然后我们给p1这个对象新加了一个age属性,并且设置成只读的。这样我们就无法修改这个age属性了。
Object.defineProperty(p1,"age",{
writable :false,//表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
value : 15,
})
console.log(p1.age); //15
p1.age = 20;
console.log(p1.age); //15
//在通过这个方法给enumerable设置为false,这样对象就不能通过迭代器遍历出age这个属性的值了。
Object.defineProperty(p1,"age",{
enumerable:false,//表示能否通过for in循环访问属性,默认值为false
})
for(var i in p1){
console.log(p1[i]);
} // lisi
var book = {
_year : 2004,
edition : 1
}Object.defineProperty(book,"year",{
get: function(){
return this._year
},
set: function(newYear){
if(newYear > 2004){
this._year = newYear;
this.edition += newYear - 2004
}
}
})book.year = 2005;
console.log(book.edition); // 2
console.log(book._year); //2005
由于get方法返回_year的值,set方法通过计算来确定正确的版本。
因此把year的值设置为2005会导致edition的值变为2
var student = {};
Object.defineProperties(student,{
name:{
writable:false,
value:"lisi"
},
age : {
writable:true,
value : 16,
},
sex:{
get(){
return '男';
},
set(v){
p1.sex = v
}
}
})
student.sex = "女";
console.log(student.sex); //男
console.log(p1.sex); // 女
附上一个模拟Vue中v-module数据双向绑定原理的代码:
Document
let arr = [7, 8, 9, 1, 2, 3, 4, 5, 6];
sort(arr);
function sort(arr) {
arr.forEach((item,index)=>{
//从第二个开始
while (index > 0 && item < arr[index - 1]) {
arr[index] = arr[index - 1];
index--;
}
arr[index] = item;
})
console.log(arr)
}
a++称为“后增量”,++a称为“前增量”。
两者在使用中有一些区别,比如:
1、
let a = 1;
const b = a++; // b = 1
2、
let a = 1
const b = ++a; // b = 2
举个例子: