uniappx+utx学习

都是散记一些坑和注意点,建议先过一遍ts再来看这个容易理解

定义类型的方式:

let a:string|null =5
if(a!=null)

const datas: Date = new Date()

map类型:

const map1:Map = new Map()
map1.set(‘key1′,’abc’)

普通定义对象


type ops = {
count: number
result:string

}

const datt = JSON.parse(‘{“result”:’呜呜呜’,”count”:42}’) as ops

// JSON.parse转换后的对象是没有类型的,直接用报错,要用as类型断言去定义他的类型

datt.count // 调用方法,推荐使用 datt[‘count’],注意这里哪怕你只需要count但是result的值你还要定义

你可能注意到,上面定义对象都要先定义一个type,很是麻烦,所以出来了UTSJSONObject类型

UTSJSONObject类型

const datt = JSON.parse(‘{“result”:’呜呜呜’,”count”:42}’) as UTSJSONObject

获取值用getXXXX方法来,如:getBoolean(‘属性名’)

const oll = datt.getString(‘result’)
const oll2 = datt.getNumber(‘count’)

// 上面含义是获取 某某 值,同时声明为 某某 类型

类型转换

都是用toXXX方法来转换,如:toInt()

let a:number = 3

a.toInt() // 转换为 Int 类型。注意和parseInt、Math.floor的区别。

a.toFloat() // 转换为 Float 类型,后续也将支持 new Float(a) 方式转换

a.toDouble() // 转换为 Double 类型,后续也将支持 new Double(a) 方式转换

// 转换为 Byte 类型,超出-128至+127会溢出,结果为:-31

let b = (225 as Number).toByte()

//平台专有类型之间,也可以使用to方法转换

let i:Int = 1 ;

i.toDouble() // 转换为 Double 类型

专有数字类型,转换为number,使用Number.from()方法

let a: Int = 1

let a1 = Number.from(a) // Int转number

let b: Float = 3.14.toFloat()

let b1 = Number.from(b)

let c: Double = 3.1414926

let c1 = Number.from(c)

let e: Long = 2147483649

let e1 = Number.from(e)

空值合并

空值合并运算符(??)是一个逻辑运算符,当左侧的操作数为 null 时,返回其右侧操作数,否则返回左侧操作数。

const foo = null ?? ‘default string’;

console.log(foo);

// Expected output: “default string”

const baz = 0 ?? 42;

console.log(baz);

// Expected output: 0

定义数组(注意)

方法1:

const a1: string[] = [‘a’, ‘b’, ‘c’]; //表示数组内容都是string

方法2:

const a1:Array<string> = [“uni-app”, “uniCloud”, “HBuilder”] //表示数组内容都是string。如不能确定可以写Array<any>

let a2:Array<number> = []; //定义一个数字类型的空数组

方法3:

let a1 = new Array(1,2,3);//支持

let a2 = new Array(1,’2′,3);//安卓平台支持, iOS 平台不支持,在 iOS 中创建 Any[] 数组请直接使用数组字面量,如 let a2 = [1. ‘2’, 3]

let a3 = Array(1,2,3);//支持

let a4 = Array(1,’2′,’3′);//安卓平台支持, iOS 平台不支持,在 iOS 中创建 Any[] 数组请直接使用数组字面量,如 let a4 = [1,’2′,’3′]

uvue的data定义数组

export default {
	data() {
		return {
			listdata: [] as Array<UTSJSONObject>,
		}
	}
}

一些写法参考:

let jo: UTSJSONObject[] = [{
	"x": 1,
	"y": 2
}] //正确,数组赋值给数组



let jo: Array<UTSJSONObject> = [{
	"x": 1,
	"y": 2
}] //正确,数组赋值给数组


let jo = [{
	"x": 1,
	"y": 2
}] //正确,自动推断为 UTSJSONObject 数组

下面是一些注意点,杂记:

官方将backgroundColor 改为 backgroundColorContent

如果编译器不知道你的类型,请手动as进行类型断言

泛型<T>,相当于一个占位替代

当你真用这个方法时再决定要传什么参数类型

(void标识表示没返回)

function ops<T>(arg: T):void{

Console.log(arg)

}

ops<number>(55)   // 注意 它也支持自动类型推断

ops(55)

//上面是一样的效果

泛型约束(限制泛型范围,用extends)

Function test<T extends Array<any>>(arg: T):T{

Return arg

}

Const oo = test([1,2,3])

list-item上不能用v-if(好像修复了)

uni.getStorageSync同步获取数据,会阻塞应用

uni.getStorage异步获取数据

uni.getStorage({
					key: key,
					success: (res: GetStorageSuccess) => {
					
						const list = JSON.parseArray<Item>(res.data as string)
						if (list != null) {
							this.list = list
						}
					}
				})

现在获取JSON.parseArray(res.data as string)这样就行 以前的写法是:JSON.parseArray<Item>(JSON.stringify(res.data))

定义带类型的 emits

const emits = defineEmits<{  (event: ‘reply’, obj: ReplyEmit): void}>()

const emits = defineEmits<{ (event: 'reply', obj: ReplyEmit): void }>();

function reply() {
  const data: ReplyEmit = { id: '123', message: 'Hello, World!' };
  emits('reply', data);
}
//下面是父组件
<MyComponent @reply="handleReply" />
methods: {
  handleReply(obj) {
    console.log(obj.id);       // '123'
    console.log(obj.message);  // 'Hello, World!'
  }
}
注意:
类型声明 { (event: 'reply', obj: ReplyEmit): void }
这里使用了 TypeScript 的函数类型定义,描述了这个组件可以触发的事件及其参数类型。

event: 'reply':指定该组件可以发射一个名为 'reply' 的事件。这个事件名称是字符串类型 'reply',表示在使用组件时,父组件可以监听 reply 事件。

obj: ReplyEmit:reply 事件的第二个参数是 obj,类型为 ReplyEmit。这意味着触发 reply 事件时,会传递一个类型为 ReplyEmit 的对象。这个对象类型 ReplyEmit 需要在你的代码中定义好,可以包含你需要传递的具体数据。

void:表示这个函数没有返回值,即触发事件后不会有返回值给调用者。

onLoad 必须有参数 没有的话会报错,还要放在script最下面,不然容易找不到某些方法

箭头函数必须有大括号

uniapp踩坑记录

1. 页面路径和文件名大小写问题

// 假设你有一个页面路径是 '/pages/Home/index'
this.$router.push('/pages/home/index'); // 小写的 'home' 会导致找不到页面

2. 小程序平台差异

// 微信小程序中使用
uni.getSystemInfo({
  success: (res) => {
    console.log(res);
  }
});
// 支付宝小程序中使用
if (uni.getSystemInfoSync().platform === 'devtools') {
  // 特殊处理支付宝小程序
}

3.uniapp跨页面传值

  • 使用 URL 参数传值
  • 使用全局状态管理(Vuex)
  • 全局对象传值
  • 本地存储传值
  • 使用事件总线(Event Bus)
  • getOpenerEventChannel 传参
  1. 使用 URL 参数传值
uni.navigateTo({
  url: '/pages/detail/detail?id=123&name=Alice'
});
//接受页面
onLoad(options) {
  const { id, name } = options;
  console.log(id, name); // 输出: 123 'Alice'
}

2. 全局对象传值 ( 可以使用全局对象 getApp() 或全局变量。 )

const app = getApp();
app.globalData.userInfo = { id: 123, name: ‘Alice’ };

uni.navigateTo({
url: ‘/pages/detail/detail’
});
// 接受页

onLoad() {
const app = getApp();
const userInfo = app.globalData.userInfo;
console.log(userInfo); // 输出: { id: 123, name: ‘Alice’ }
}

3. 本地存储传值 ( 适用于需要跨页面、甚至跨应用会话的数据传递。 )

uni.setStorageSync('userInfo', { id: 123, name: 'Alice' });

uni.navigateTo({
  url: '/pages/detail/detail'
});
//接受页面
onLoad() {
  const userInfo = uni.getStorageSync('userInfo');
  console.log(userInfo); // 输出: { id: 123, name: 'Alice' }
}

3. bus传值

// eventBus.js
    import Vue from 'vue';
    export default new Vue();
    

    **传值页面:**

    ```javascript
    import eventBus from '@/eventBus';
    eventBus.$emit('sendUserInfo', { id: 123, name: 'Alice' });
    uni.navigateTo({
      url: '/pages/detail/detail'
    });
    ```

    **接收值页面:**

    ```javascript
    import eventBus from '@/eventBus';

    onLoad() {
      eventBus.$on('sendUserInfo', (userInfo) => {
        console.log(userInfo); // 输出: { id: 123, name: 'Alice' }
      });
    }
    

4. getOpenerEventChannel 传参

组件a给B传

uni.navigateTo({
url: ‘/pages/PageB/PageB’,
events: {
// 事件回调函数
acceptDataFromOpenerPage: function(data) {
console.log(data)
},

ok: function(res) {
if(options.ok) options.ok(res.data);
},


},
success: function(res) {
// 通过eventChannel向被打开页面传递数据
res.eventChannel.emit(‘acceptDataFromOpenerPage’, { data: ‘test data from PageA’ })
}
})

PageB 中,通过 getOpenerEventChannel 获取事件通道,并监听从 PageA 传递的数据。

export default {
onLoad() {
const eventChannel = this.getOpenerEventChannel();

// eventChannel.emit(‘ok’, { data: data }); 这是触发event里面设置的事件回调函数, 返回事件
eventChannel.on(‘acceptDataFromOpenerPage’, function(data) {
console.log(data); // { data: ‘test data from PageA’ } 这里是获取
});
}
}

uni.navigateTo:用于跳转到新页面,同时传递一个 events 对象,用于定义事件回调函数。

eventChannel.emit:在跳转成功后的 success 回调中,通过 eventChannel 发射事件,传递数据给新页面。

getOpenerEventChannel:在新页面中调用,获取事件通道,并通过 eventChannel.on 监听事件,接收数据。

uniapp跳转的几种方式

  1. uni.navigateTo:保留当前页面,跳转到应用内的某个页面,使用 `uni.navigateBack` 可以返回到原页面。 “`javascript uni.navigateTo({ url: ‘/pages/detail/detail?id=123&name=Alice’ });

2. uni.redirectTo:关闭当前页面,跳转到应用内的某个页面。 “`javascript uni.redirectTo({ url: ‘/pages/detail/detail?id=123&name=Alice’ });

3. uni.switchTab:跳转到 `tabBar` 页面,并关闭其他所有非 tabBar页面。 uni.switchTab({ url: '/pages/tabbar/index' });

4. uni.reLaunch:关闭所有页面,打开到应用内的某个页面。 javascript uni.reLaunch({ url: ‘/pages/detail/detail?id=123&name=Alice’ });

5. uni.navigateBack:关闭当前页面,返回上一页面或多级页面。 `javascript uni.navigateBack({ delta: 1 // 返回一级页面 }); `

前端下载文件(exe,zip,js等)的几种方式

闲来无事,总结一下这些东西:

前端一般通过创建a标签下载文件

图片:分为本地和后端传,本地直接require引入路径后创建a标签下载:

private downFun() {
    let a=document.createElement('a')
    a.style.display = 'none'
    a.setAttribute('download', 'bg.jpg')
    a.href = require('@/assets/bg.jpg')
    document.body.appendChild(a)
    console.log('href', a.href)
    a.click()
    document.body.removeChild(a)
  }

后端传的话需要调用get接口。

其它文件类型下载基本都是一样的 调用后端get接口,使用blob类型

responseType: ‘blob’。进行下载

注意:如果要是本地下载一些静态资源(除图片外) 资源要放在public目录文件下,不然打包后路径你会发现很多报错,下载找不到路径。@这个不会被解析。

附下载代码:

async download(){
    console.log(this.urlName)
    let res = await axios.get(`jt/${this.urlName}`, { responseType: 'blob' })
    const url = window.URL.createObjectURL(res.data)
    const link = document.createElement('a')
    link.href = url;
    link.setAttribute('download', `${this.urlName}`)
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  }

jt/${this.urlName}

jt是public下的一个文件夹, this.urlName是你要下载的文件的名字。

vue项目权限,路由设计

现在我项目用的方法

路由设计:路由表中会定义两个路由表,一个为公共路由表,一个为动态路由表。动态路由根据登录账户的权限去获取。取到权限后,根据roles中的字段,去遍历动态路由表中的对应的路由表。

最后将获取到的符合权限的路由,通过router.addRouter()合并到固定路由下面,,再用到router.beforeEach()去做路由判断,让权限更牢靠

常用方法

第一种

1.对路由跳转进行判断,如果符合权限就允许,反之就不行

2.对跳转页面进行逻辑请求判断,就是页面数据需要一定的权限才能发送请求(这样需要后端人员给你做,个人感觉不太现实,后端估计想干你)

3.根据权限,动态生成对应的路由,什么权限拥有什么路由(vue-element-admin)就是这么做的,动态生成路由

前端常用方法一般是:  v-if + router.beforeEach()

由于后端管理界面涉及到,对登陆用户的权限判断,可能做的好点的后端管理页面,要么把相对应的跳转事件进行隐藏或判断,或者把跳转页面进行隐藏,我之前也是这样做的,对跳转的路由按钮进行  v-if  判断,然后再在router.beforeEach()进行路由判断,这样就可以防止部分用户,根据请求地址来实现跳转

第二种

对跳转的按钮事件进行权限判断


对跳转页面的路由事件,添加一个方法,然后根据权限判断,if和else,满足就跳转,不满足就return,如果想要提升一下用户体验度,就弹出一个消息提示框,说您暂无权限

但是呢,用户体验度不是很好,而且权限是写死的,不具备灵活性

第三种

根据权限动态生成路由

第一步:

就是在你登陆以后,后端返回token,然后在请求成功的回调里面,又发送token去后端去获取当前用户的详细信息,信息中包括了你这名用户的权限,是否为管理员身份,还是次级管理员身份,然后将token存入cookie,将请求获取到的数据存入vuex!假设这个存储你权限的字段交roles,是个数组,类似:[’admin’] or [‘Secondary’] 

第二步:

这里要说明的是,路由表中会定义两个路由表,一个为公共路由表,一个为动态路由表,公共路由表就是不需要权限也能去访问,动态路由表顾名思义就是需要权限去获取了!然后根据获取到你的权限之后,会根据roles中的字段,去遍历动态路由表中的对应的路由表,然后将符合条件的路由表保存起来

第三步:最后将获取到的符合权限的路由,合并到固定路由下面,通过router.addRouter(),当然了这里还是要用到router.beforeEach()去做路由判断,让权限更牢靠一点

参考文献:

http://events.jianshu.io/p/1bbecc010fae

return返回html标签的问题

当你使用return返回如:<span class=’mtClass’>标签</span>

此类的话,返回时可能由于你的js运行在vue的实例化之后,这样它的渲染是无法渲染组件标签的。如elementUI的el-button.

若这时你返回<el-button>按钮</el-button>,在页面渲染时,它会直接标签化渲染成
<el-button>按钮</el-button>

正确应该是要渲染成<button class=’el-button’>按钮</button>

解决办法:

1.你直接用html的标签 然后自己写样式改

2.用render函数 写jxs代码

JSX是JavaScript XML的简写,表示在JavaScript代码中写XML ( HTML)格式的代码。


Vue渲染组件底层用的是render函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 31</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  // render function
  // template -> render -> h -> 虚拟DOM(JS对象)-> 真实 DOM -> 展示到页面上
  const app = Vue.createApp({
    template: `
      <my-title :level="2">
        hello dell
      </my-title>
    `
  });

  app.component('my-title', {
    props: ['level'],
    render() {
      const { h } = Vue;
      return h('h' + this.level, {}, [
        this.$slots.default(),
        h('h4', {}, 'dell')
      ])
    }
  })

  const vm = app.mount('#root');
</script>
</html>

vue3快速学习(vue2转vue3学习)

 首先要知道的一些方法,基础api。避免看不懂vue3。

1.    reactive

返回对象的响应式副本,实际上reactive 将解包所有深层的 refs,同时维持 ref 的响应性。

个人理解:相当于把reactive里面的内容a,push到一个对象中,而且还互相相应。我称这个对象为依赖于a的副本。内容为空,后续通过  obj.count = count  这样的写法,也能将其绑定响应。

const obj = reactive({ count: 0 })
响应式转换是“深层”的——它影响所有嵌套 property。在基于 ES2015 Proxy 的实现中,返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。

类型声明:

function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

例子:

const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

// 它会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 它也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

当将 ref 分配给 reactive property 时,ref 将被自动解包。

const count = ref(1)
const obj = reactive({})

obj.count = count

console.log(obj.count) // 1
console.log(obj.count === count.value) // true


2.    readonly

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

和reactive一样也是push给一个对象,但是他这个对象是不能改变它的值的。它本身是可以改变的,而且如果给这个对象设置watch了的话,还会触发它的监听器。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用于响应性追踪
  console.log(copy.count)
})

// 变更 original 会触发依赖于副本的侦听器
original.count++

// 变更副本将失败并导致警告
copy.count++ // 警告!
与 reactive 一样,如果任何 property 使用了 ref,当它通过代理访问时,则被自动解包:

const raw = {
  count: ref(123)
}

const copy = readonly(raw)

console.log(raw.count.value) // 123
console.log(copy.count) // 123

3.    isProxy

检查对象是否是由 reactive 或 readonly 创建的 proxy。


4.    isReactive

检查对象是否是由 reactive 创建的响应式代理

注意:从普通对象创建的只读 proxy,用isReactive检查会是false.如: const plain = readonly({ name: ‘Mary’ })    console.log(isReactive(plain)) // -> false

例子:

import { reactive, isReactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    console.log(isReactive(state)) // -> true
  }
}

如果该代理是 readonly 创建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true。

import { reactive, isReactive, readonly } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    // 从普通对象创建的只读 proxy
    const plain = readonly({
      name: 'Mary'
    })
    console.log(isReactive(plain)) // -> false

    // 从响应式 proxy 创建的只读 proxy
    const stateCopy = readonly(state)
    console.log(isReactive(stateCopy)) // -> true
  }
}


5.    isReadonly

检查对象是否是由 readonly 创建的只读代理。


6.    toRaw

返回 reactive 或 readonly 代理的原始对象。这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。
不建议保留对原始对象的持久引用。请谨慎使用。例子:

const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true


7.    markRaw

标记一个对象,使其永远不会转换为 proxy。返回对象本身。
也就是它设置的值,对象,数组等。是不是响应的。

const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 嵌套在其他响应式对象中时也可以使用
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false


重要

markRaw 和下方的 shallowXXX API 使你可以有选择地退出默认的深度响应式/只读转换模式,并将原始的,未被代理的对象嵌入状态图中。它们可以根据情况灵活运用:

有些值不应该是响应式的,例如复杂的第三方类实例或 Vue 组件对象。
当渲染具有不可变数据源的大列表时,跳过 proxy 转换可以提高性能。


这些例子是进阶的运用,因为原始选择退出仅在根级别,因此,如果将嵌套在内的、未标记的原始对象添加进响应式对象,然后再次访问该响应式对象,就会得到原始对象被代理后的版本。这可能会导致同一性风险——即执行一个依赖于对象本身的操作,但同时使用同一对象的原始版本和被代理后的版本:

const foo = markRaw({
  nested: {}
})

const bar = reactive({
  // 虽然 `foo` 被标记为原始,但 foo.nested 不是。
  nested: foo.nested
})

console.log(foo.nested === bar.nested) // false


同一性风险通常很少见。然而,为了正确地使用这些 API,同时安全地避免同一性风险,就需要对响应性系统的工作原理有一个充分的理解。


8.    shallowReactive

创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
个人理解:类似浅拷贝。

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式

与 reactive 不同,任何使用 ref 的 property 都不会被代理自动解包。


9.    shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。

const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的 property 将失败
state.foo++        // 失败
// ...但适用于嵌套对象
isReadonly(state.nested) // false
state.nested.bar++ // 适用


与 readonly 不同,任何使用 ref 的 property 都不会被代理自动解包。


下面是主要使用的api

Refs

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。
示例:

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

注意:如果将对象分配为 ref 值,则它将被 reactive 函数处理为深层的响应式对象。可以连着上面的reactive一起看着理解。

类型声明:

interface Ref<T> {
  value: T
}

function ref<T>(value: T): Ref<T>

有时我们可能需要为 ref 的内部值指定复杂类型。可以在调用 ref 时传递一个泛型参数以覆盖默认推断,从而简洁地做到这一点:

const foo = ref<string | number>('foo') // foo 的类型:Ref<string | number>

foo.value = 123 // ok!

如果泛型的类型未知,则建议将 ref 转换为 Ref<T>:

function useState<State extends string>(initial: State) {
  const state = ref(initial) as Ref<State> // state.value -> State extends string
  return state
}

Ref 解包


当 ref 作为渲染上下文 (从 setup() 中返回的对象) 上的 property 返回并可以在模板中被访问时,它将自动浅层次解包内部值。只有访问嵌套的 ref 时需要在模板中添加 .value:

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count ++">Increment count</button>
    <button @click="nested.count.value ++">Nested Increment count</button>
  </div>
</template>

<script>
  import { ref } from 'vue'
  export default {
    setup() {
      const count = ref(0)
      return {
        count,

        nested: {
          count
        }
      }
    }
  }
</script>

如果你不想要访问实际的对象实例,可将其用 reactive 包裹:

nested: reactive({
  count
})


访问响应式对象
当 ref 作为响应式对象的 property 被访问或更改时,为使其行为类似于普通 property,它会自动解包内部值:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

如果将新的 ref 赋值给现有 ref 的 property,将会替换旧的 ref:

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1

Ref 解包仅发生在被响应式 Object 嵌套的时候。当从 Array 或原生集合类型如 Map访问 ref 时,不会进行解包:

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)


响应式状态解构
当我们想使用大型响应式对象的一些 property 时,可能很想使用 ES6 解构来获取我们想要的 property:

import { reactive } from 'vue'

const book = reactive({
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let { author, title } = book

遗憾的是,使用解构的两个 property 的响应性都会丢失。对于这种情况,我们需要将我们的响应式对象转换为一组 ref。这些 ref 将保留与源对象的响应式关联:

import { reactive, toRefs } from 'vue'

const book = reactive({
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let { author, title } = toRefs(book)

title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'

isRef

检查值是否为一个 ref 对象。

unref

如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。

这个一般用在ts文件里面。

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // unwrapped 现在一定是数字类型
}


toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

一般用来对某个对象里面的一个属性进行一个响应链接。

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

当你要将 prop 的 ref 传递给复合函数function时,toRef 很有用:

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))    // 把props继承的对象中的foo属性传入useSomeFeature这个函数。
  }
}

即使源 property 不存在,toRef 也会返回一个可用的 ref。这使得它在使用可选 prop 时特别有用,可选 prop 并不会被 toRefs 处理。


toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref

结合 ref对象 仅有一个 .value property,指向该内部值。根据原型链就能理解下面     ref 和原始 property 已经“链接”起来了    这句话了.

更简单理解toRef只能传一个,toRefs能传多个。

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3


当从组合式函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行解构/展开:

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // 操作 state 的逻辑

  // 返回时转换为ref
  return toRefs(state)
}

export default {
  setup() {
    // 可以在不失去响应性的情况下解构
    const { foo, bar } = useFeatureX()

    return {
      foo,
      bar
    }
  }
}


customRef

创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。

使用自定义 ref 通过 v-model 实现 debounce 的示例:

<input v-model="text" />

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello')
    }
  }
}


类型声明:

function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}

shallowRef


创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

const foo = shallowRef({})
// 改变 ref 的值是响应式的
foo.value = {}
// 但是这个值不会被转换。
isReactive(foo.value) // false

triggerRef


手动执行与 shallowRef 关联的任何作用 (effect)。

const shallow = shallowRef({
  greet: 'Hello, world'
})

// 第一次运行时记录一次 "Hello, world"
watchEffect(() => {
  console.log(shallow.value.greet)
})

// 这不会触发作用 (effect),因为 ref 是浅层的
shallow.value.greet = 'Hello, universe'

// 记录 "Hello, universe"
triggerRef(shallow)

vue2中的创建变量 在3中写法是
props是你上一层组件穿过来的值,你可以自己在这接受,并且定义其类型。如:
props: {
    user: {
      type: String,
      required: true
    }
  },

回归正题

setup(props){
    const a = ref(0)
}

下面开始分析项目中的一些写法:

vue2在data里定义的a,在vue3中这么定义,ref里面定义其初始值。如:a:9, 在vue3写法就是const a = ref(9)watch的写法变成

setup(props){
    const a = ref(0)

watch(counter, (newValue, oldValue) => {
  console.log('The new counter value is: ' + counter.value)
})

// 到最后要讲里面写的方法和变量return回来

return {a}
}

独立的 computed 属性

const twiceTheCounter = computed(() => counter.value * 2)
return {twiceTheCounter}


下面是最新总的写法

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'

// in our component
setup (props) {
  // 使用 `toRefs` 创建对 props 中的 `user` property 的响应式引用
  const { user } = toRefs(props)

  const repositories = ref([])
  const getUserRepositories = async () => {
    // 更新 `props.user ` 到 `user.value` 访问引用值
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)

  // 在用户 prop 的响应式引用上设置一个侦听器
  watch(user, getUserRepositories)

  const searchQuery = ref('')
  const repositoriesMatchingSearchQuery = computed(() => {
    return repositories.value.filter(
      repository => repository.name.includes(searchQuery.value)
    )
  })

  return {
    repositories,
    getUserRepositories,
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}

使用 setup 函数时,它将接受两个参数:

props
context

setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

例子:

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}


如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来完成此操作

const { title } = toRefs(props)

使用toRefs解绑后会安全消除 prop 的响应性。如:

const { title } = toRefs(props)

console.log(title.value)

context 是一个普通的 JavaScript 对象,它暴露三个组件的 property:

// MyBook.vue

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    console.log(context.attrs)

    // 插槽 (非响应式对象)
    console.log(context.slots)

    // 触发事件 (方法)
    console.log(context.emit)
  }
}


它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构。

// MyBook.vue
export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}


attrs slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。
请注意,与 props 不同,attrs 和 slots 是非响应式的。
如果你打算根据 attrs 或 slots 更改应用副作用,那么应该在 onUpdated 生命周期钩子中执行此操作

执行 setup 时,组件实例尚未被创建。因此,你只能访问以下 property:

props
attrs
slots
emit

换句话说,你将无法访问以下组件选项:

data
computed
methods

注意,从 setup 返回的 refs 在模板中访问时是被自动解开的,因此不应在模板中使用 .value

setup 还可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态:

// MyBook.vue

import { h, ref, reactive } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // Please note that we need to explicitly expose ref value here
    return () => h('div', [readersNumber.value, book.title])
  }
}

注册组件


#2.x 语法
在 2.x 中,注册一个组件后,把组件名作为字符串传递给渲染函数的第一个参数,它可以正常地工作:// 2.x

Vue.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      Clicked {{ count }} times.
    </button>
  `
})

export default {
  render(h) {
    return h('button-counter')
  }
}


#3.x 语法
在 3.x 中,由于 VNode 是上下文无关的,不能再用字符串 ID 隐式查找已注册组件。取而代之的是,需要使用一个导入的 resolveComponent 方法:// 3.x

import { h, resolveComponent } from 'vue'

export default {
  setup() {
    const ButtonCounter = resolveComponent('button-counter')
    return () => h(ButtonCounter)
  }
}

Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件。

在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件:

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text']
  }
</script>


vue3.的行为
和 prop 类似,现在可以通过 emits 选项来定义组件可触发的事件:

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text'],
    emits: ['accepted']
  }
</script>


vue3新增:现在可以在同一个组件上使用多个 v-model 绑定;
新增:现在可以自定义 v-model 修饰符。

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<!-- 是以下的简写: -->

<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>


2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用。

#3.x 语法


v-if 会拥有比 v-for 更高的优先级。


在 2.x 中,如果一个元素同时定义了 v-bind=”object” 和一个相同的独立 attribute,那么这个独立 attribute 总是会覆盖 object 中的绑定。

<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="red"></div>


在 3.x 中,如果一个元素同时定义了 v-bind=”object” 和一个相同的独立 attribute,那么绑定的声明顺序将决定它们如何被合并。

换句话说,相对于假设开发者总是希望独立 attribute 覆盖 object 中定义的内容,现在开发者能够对自己所希望的合并行为做更好的控制。

<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>

<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>

实用技巧

script setup是vue3中新引入的语法糖,目的是简化使用Composition API时冗长的模板代码。
如:

<script lang="ts">
import { defineComponent, ref } from 'vue'
import { MyButton } from '@/components'
 
export default defineComponent({
  components: {
    MyButton
  },
  props: {
      name: String
  },
  setup() {
    const count = ref(0)
    const inc = () => count.value++
 
    return {
      count,
      inc,
    }
  },
})
<script>

当我们需要引入一个components时,不仅需要在文件头部显式import进行导入,而且需要components字段加入声明。

不仅如此,在setup中声明的变量如果需要被模板使用,那么需要在setup的尾部显式return返回,如果你的组件模板使用的变量不多,还可以勉强接受。但是当你的变量和方法逐渐增加时,每次都要在setup后进行return返回,这无疑是一件枯燥的事情,在重构代码时,你也会面临巨大挑战。

为了解决这个问题,vue3添加了script setup语法糖提案。

像上面这段代码,使用script setup语法糖重构后,将变成:

<script setup lang="ts">
import { defineComponent, ref, defineProps } from 'vue'
import { MyButton } from '@/components'
 
defineProps<{
    name:string
}>()
 
const count = ref(0)
const inc = () => count.value++
 
<script>


基本用法
若要使用script setup语法,只需在原vue文件中的script标签加入setup属性。

<script setup lang=”ts”>
 
<script>

使用后意味着,script标签内的内容相当于原本组件声明中setup()的函数体,不过也有一定的区别。

使用setup中的参数
<script setup=”props, context” lang=”ts”>
 
<script>


像这样,只要在setup处声明即可自动导入,同时也支持解构语法:

<script setup=”props, { emit }” lang=”ts”>
 
<script>

还有一种写法 每次return的时候,结合三点运算符实现

setup() {
    const state = reactive({
        listParam: {
          searchKey: “”,
          mainFollower: “”
        }
    })
    watch([() => state.listParam.customerName, () => state.listParam.mainFollower],
      ([newCustomerName, newMainFoller],[oldCustomerName,oldMainFoller]) => {
        state.listParam.customerName = newCustomerName.trim()
        state.listParam.mainFollower = newMainFoller.trim()
    },{
       immediate: true
    })
    return {
        …toRefs(state)  // 三点运算符实现多个返回
    }
}

await语法支持
在script setup内可以直接使用await语法:

<script setup>
  const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>

Computed 与 watch

computed

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变响应式 ref 对象。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

或者,接受一个具有 get set 函数的对象,用来创建可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

类型声明:

// 只读的
function computed<T>(
  getter: () => T,
  debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>

// 可写的
function computed<T>(
  options: {
    get: () => T
    set: (value: T) => void
  },
  debuggerOptions?: DebuggerOptions
): Ref<T>
interface DebuggerOptions {
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}
interface DebuggerEvent {
  effect: ReactiveEffect
  target: any
  type: OperationTypes
  key: string | symbol | undefined
}

用ts的泛类,你可以自己定义computed的类型

watchEffect

立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
  count.value++
  // -> logs 1
}, 100)


使用watchEffect监听数据,可以单个或多个,不需要传入监听的数据源,而是直接执行里面的方法,获取到更新后的值

vue文件
<el-form :model="listParam" label-position="left" inline>
    <el-form-item prop="searchKey">
       <el-input v-model="listParam.searchKey" placeholder="请输入关键字" clearable size="small"></el-input>
    </el-form-item>
    <el-form-item prop="mainFollower">
      <el-input v-model="listParam.mainFollower" placeholder="请输入跟进人姓名" clearable size="small"></el-input>
    </el-form-item>
</el-form>

js文件

setup() {
    const state = reactive({
        listParam: {
          searchKey: "",
          mainFollower: ""
        }
    })
    watchEffect(() => {
      state.listParam.searchKey = state.listParam.searchKey ? state.listParam.searchKey.trim() : ""
      state.listParam.mainFollower= state.listParam.mainFollower? state.listParam.mainFollower.trim() : ""
    })
    return {
        ...toRefs(state)
    }
}

类型声明:

function watchEffect(
  effect: (onInvalidate: InvalidateCbRegistrator) => void,
  options?: WatchEffectOptions
): StopHandle

interface WatchEffectOptions {
  flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

interface DebuggerEvent {
  effect: ReactiveEffect
  target: any
  type: OperationTypes
  key: string | symbol | undefined
}

type InvalidateCbRegistrator = (invalidate: () => void) => void

type StopHandle = () => void


watch

watch可以获取改变前后的值,watchEffect不可以

watch API 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。

与 watchEffect 相比,watch 允许我们:

惰性地执行副作用;
更具体地说明应触发侦听器重新运行的状态;
访问被侦听状态的先前值和当前值。


watch侦听单一源
侦听器数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref:

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})


watch侦听多个源
侦听器还可以使用数组以同时侦听多个源:

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})


[fooRef, barRef]这是你要监听的2个变量。


[foo, bar], [prevFoo, prevBar],这是2个改变前后监听到的值。

例子:

监听单一数据:

setup() {
    const state = reactive({
        listParam: {
          searchKey: ""
        }
    })
    watch(() => state.listParam.searchKey, (newVal,oldVal) => {
        console.log(newVal, oldVal)
        state.listParam.searchKey = newVal.trim()
    })
    return {
        ...toRefs(state)
    }
}


监听多数据:

setup() {
    const state = reactive({
        listParam: {
          searchKey: "",
          mainFollower: ""
        }
    })
    watch([() => state.listParam.customerName, () => state.listParam.mainFollower],
      ([newCustomerName, newMainFoller],[oldCustomerName,oldMainFoller]) => {
        state.listParam.customerName = newCustomerName.trim()
        state.listParam.mainFollower = newMainFoller.trim()
    },{
       immediate: true
    })
    return {
        ...toRefs(state)
    }
}


与 watchEffect 相同的行为
watch 与 watchEffect 在手动停止侦听、清除副作用 (将 onInvalidate 作为第三个参数传递给回调)、刷新时机和调试方面有相同的行为。

类型声明:

// 侦听单一源
function watch<T>(
  source: WatcherSource<T>,
  callback: (
    value: T,
    oldValue: T,
    onInvalidate: InvalidateCbRegistrator
  ) => void,
  options?: WatchOptions
): StopHandle

// 侦听多个源
function watch<T extends WatcherSource<unknown>[]>(
  sources: T
  callback: (
    values: MapSources<T>,
    oldValues: MapSources<T>,
    onInvalidate: InvalidateCbRegistrator
  ) => void,
  options? : WatchOptions
): StopHandle

type WatcherSource<T> = Ref<T> | (() => T)

type MapSources<T> = {
  [K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}

// 参见 `watchEffect` 共享选项的类型声明
interface WatchOptions extends WatchEffectOptions {
  immediate?: boolean // 默认:false
  deep?: boolean
}

webpack和vue3结合安装

vue3 学习网站

https://24kcs.github.io/vue3_study/chapter1/04_webpack%E6%89%93%E5%8C%85.html#%E4%B8%8B%E8%BD%BD%E4%BE%9D%E8%B5%96

里面也有webpack配置打包教程

最新的webpack兼容不好 这里安装webpack时要用其他版本

命令语句

npm install -D webpack@4.41.5 webpack-cli@3.3.10 webpack-dev-server@3.10.2

下面版本也要一致
npm install -D html-webpack-plugin@4.5.0 clean-webpack-plugin@3.0.0

npm install -D ts-loader@8.0.11 cross-env@7.0.2

这样开发前的环境就可以了

vue+echarts大屏地图以及在大屏上画线和数值(最新echart5.0)

最近公司要求开发一个大屏项目做到 :

直接上代码

首先做出地图效果

https://datav.aliyun.com/tools/atlas/#&lat=30.332329214580188&lng=106.72278672066881&zoom=3.5

上面链接里获取你想要的城市,省份json数据,再将其放入你的项目文件中

下面核心代码:

 
  <div class="echart_map" ref="hunan_map">
 
 
 
 
var hunan = require(`../../assets/js/湖南省.json`)  // 引入json
methods: {
 
 // echart单位换算
    nowSize (val,initWidth) {
      initWidth = 1920
      return val * (this.c_width / initWidth);
    },
 huNanMap (val) {
      var that = this
      this.$echarts.registerMap('hunan', hunan); // 取数据
      var myChart = this.$echarts.init(this.$refs.hunan_map)
 
      var data = json.data;
      var series = [];
 
        // 画线,点的函数
        var convertLineData = function (data) {
        var res = [];
        for (var i = 0; i < data.length; i++) {
          var dataItem = data[i];
          var coordS = dataItem.lineS; // 线起点
          var coordM = dataItem.lineM; // 线中间点
          var coordE = dataItem.lineE; // 线尾点
          if (coordS && coordM && coordE) {
            res.push({
              coords: [coordS, coordM, coordE]
            });
          }
        }
        return res;
      };
      var convertValData = function (data) {
        var res = [];
        for (var i = 0; i < data.length; i++) {
          var dataItem = data[i];
          res.push({
            name: dataItem.areaName,
            value: dataItem.lineE.concat(dataItem.value)
          });
        }
        return res;
      };
        
        // 这里是你要在地图上画的点,线配置,往data里面加就行
        var json = {
        data: [{
          areaName: '长沙市',
          value: 94,
          lineS: [113.482279,28.19409],
          lineM: [113.441, 28.4242],
          lineE: [113.8648, 29.2891]
        }]
            };
 
      series.push(
            {    // 这是画的线配置
          name: '',
          type: 'lines',
          zlevel: 2,
          symbol: 'none',
          // silent: true, //不响应鼠标点击或事件
          effect: {
            show: false // 关闭特效
          },
          tooltip: {
            show: false
          },
          labelLayout: {
            draggable: true
          },
          polyline: true, // 支持多点连线
          itemStyle:{
            normal:{
              lineStyle:{
                width: that.nowSize(2),
                type:'dotted' //'dotted'虚线 'solid'实线
              }
            }
          },
          lineStyle: {
            normal: {
              color: 'green',
              opacity: 0.9,
              curveness: 0
            }
          },
          data: convertLineData(data)
        },
        {   // 这里是点还有点旁边的内容
          name: '',
          type: 'scatter',
          coordinateSystem: 'geo',
          zlevel: 2,
          hoverAnimation: false, // hover时不高亮点
          cursor: 'default', // 鼠标设置为箭头
          itemStyle: {
            normal: {
              color: 'red'
            }
          },
          tooltip: {
            show: false,
          },
          label: {
            rich: {
              //自定义样式a
              a: {
                fontFamily: 'lcd',   //字体名称
                color: '#fff',  //字体颜色
                fontSize: that.nowSize(12),  //字体大小
                borderColor: 'rgba(30, 157, 222, 1)',
                borderWidth: that.nowSize(1.2),
                padding: [8, 8, 8, 8]
              },
              //**自定义样式b**
              b: {
                color: '#fff',
                fontSize: 16
              }
            },
            // normal: {
            show: true,
            position: 'right',
            fontSize: that.nowSize(14),
            fontWeight: 'bold',
            color: '#f5a623',
            formatter: function (param) {
              return '{a|' + param.value[2] + '/工单' + '}' // 此处用到自定义样式
            },
           
          },
          data: convertValData(data)
        }
        )
 
        var option = {  // 这里是地图内容配置
        // backgroundColor: '#404a59', // 背景色
        title: {
          text: '全省人工服务概况',
          left: 'center',
          textStyle: {
            color: '#2b9de0',
            fontSize: that.nowSize(18)
          }
        },
        tooltip: {
          trigger: 'item'
        },
        legend: {
          show: false,
          selectorLabel: {
            backgroundColor: 'black'
          }
        },
        geo: {
          map: 'hunan',
          label: {
            color: 'black',
            fontSize: that.nowSize(14),
            emphasis: {
              show: true,
              color: 'green',
              areaColor: 'yellow'
            },
          },
          roam: false,
          selectedMode: 'single',
          itemStyle: {
            areaColor: 'red',
            normal: {
              // areaColor: 'black', // 地图整体的颜色
              borderColor: '#404a59' // 边缘线颜色
            },
            emphasis: {
              itemStyle: {
                areaColor: 'red' // hover 的背景色
              }
            }
          },
          select: [{
            itemStyle: {
              areaColor: 'black' // hover 的背景色
            }
          }
          ],
          regions: [{ // 默认选择
            name: val.name,
            selected: true,
            itemStyle: {
              normal: {
                areaColor: '#AADDFF'
              }
              // areaColor: 'red', // 地图背景色
              // backgroundColor: 'black',
              // opacity: 0
            }
          },],
        },
        series: series
      };
      myChart.setOption(option);  // 应用配置
 
       // 加个点击事件
      myChart.on('click', function (e) {
        // e.color = '#d50000'
        if (e.hasOwnProperty('region')) {
          option.geo.regions[0].name = e.region.name
          myChart.setOption(option);
        } else {
          return false;
        }
        console.log(e.region,option)
      });
}

建议线画地图出来,再一步步移植代码。

线,点或者其它什么样式基本都是label里面能改,字体样式什么的就是itemStyle里面改。

反正2个互相用就能解决大部分问题

最后formatter是个很有用的东西,在这是编辑点右边的数据。你们可以自己定义。

最后放个echart文档地址

https://echarts.apache.org/zh/option.html#title

vuex模块化管理在正式项目中的运用及其注意事项

写下平时在心目中用的比较多的vuex标准化用法和介绍

vuex一般用于中大型项目,其内容有

state 存放状态
mutations state成员操作(处理数据,更改保存到state中。用法this.$store.commit )
getters 加工state成员给外界
actions 异步操作(一般用于处理请求逻辑之后将数据给mutations,用法this.$store.dispatch )
modules 模块化状态管理
modules 的每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

简单基础使用就不说了,说重点。

在项目中一般是以模块化管理为主。为了方便每个模块的管理和维护大多使用modules模块化状态管理。

上目录

这里面的actions.js和mutation是公共的方法(我自己的习惯)

getters.js和store.js是让外界得到state,公共数据。

index.js是将所有模块集合到一起。然后挂载到main.js上。

//index.js内容
 
import Vue from 'vue'
import vuex from 'vuex'
 
 
import getters from './getters'
import actions from './modules/actions'
import mutations from './modules/mutation'
import user from './modules/user.js'
import user2 from './modules/user2'
import state from './store'
Vue.use(vuex)
export default new vuex.Store({
 
  getters,
  state,
  //模块vuex
  modules: {
    //公共vuex2个模块
    actions,
    mutations,
 
    //各个单独模块使用的vuex
    user,
    user2
  }
})

使用modules后外面不要再加actions和mutations(会报错)

上其中一个单独模块的代码

//这里是user.js的代码
 
 
//某个模块专门使用的vuex。调用时加上该模块名字。如this.$store.commit('user/方法名')
 
const state = {
  userName:''
}
 
const getters = {
  userName:(state) => state.userName
}
const actions = {
  add_name({commit},name){
    commit('ADD_NAME',name);
    console.log('这里是user1的actions',name)
 
  },
}
const  mutations = {
  ADD_NAME(state,name){
    state.userName=name;
    console.log('这里是user1',name)
    return true;
  }
}
export default {
  namespaced: true,            //记住一定要加这个
 
  state,
  getters,
  actions,
  mutations
}

namespaced是告诉vuex给它搞个看见出来。默认false. 开启后就是提示打开了命名空间。这样2个模块有同样的方法也不会报错。

当你要调用某个模块的方法时记得写法是

this.$store.commit(‘模块名/方法名’)

this.$store.dispatch(‘模块名/方法名’)

一切弄完之后记得挂载到main.js上

import Vue from 'vue'
import App from './App'
import router from './router'
import Vuex from 'vuex'
import store from './store/index'
Vue.config.productionTip = false
Vue.use(Vuex)
 
 
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,    //这就是挂载了
  components: { App },
  template: '<App/>'
})

有时候报错说未找到该模块有这个方法

报错信息为:

[vuex] unknown action type: user/add_name

你可以删除命名空间试试。

下面放其他js的代码

//actions.js代码
 
//公共actions异步方法
 
export default {//在action中可以进行异步操作。
  namespaced: true,
 
  actions:{
    add_book({commit},book){
      commit('ADD_BOOK',book);
    },
    delete_book({commit},book){
      commit('DELETE_BOOK',id);
    }
  }
}
 
 
 
//mutations.js代码
//公共mutation方法
export default {//这里要注意不要在mutations里面进行异步操作
  namespaced: true,
 
  mutations:{
    ADD_BOOK(state,book){
      state.bookList.push(book);
      return true;
    },
    DELETE_BOOK(state,id){
    }
  }
}

我觉得这样写便于管理,有的人喜欢给mutations加一个mutations_type的文件。其实就是将他们的方法名都注册一下。我不喜欢这样。

TypeScript踩坑记录

首先首先做自动编译
自己运行ts文件要用 tsc命令,如 tsc xxx.ts

想要自动编译:

第一步 tsc –init 生成tsconfig.json 改输出目录 “outDir” : “./js”(这里你要自己建立好一个js文件夹)

第二步 在vs里面任务,选择监视tsconfig.json

关于经常会犯的报错(这个写前面)
TypeScript 2.7引入了一个新的控制严格性的标记 –strictPropertyInitialization

现在,如果开启 strictPropertyInitialization,我们必须要确保每个实例的属性都会初始值,可以在构造函数里或者属性定义时赋值。

class StrictClass {
foo: number;
bar = 'hello';
baz: boolean;
// error,Property 'baz' has no initializer and is not definitely assigned in the constructor
constructor() {
this.foo = 42;
}
}

有两种情况下我们不可避免该error的产生:

  • 该属性本来就可以是 undefined 。这种情况下添加类型undefined
  • 属性被间接初始化了(例如构造函数中调用一个方法,更改了属性的值)。这种情况下我们可以使用 显式赋值断言 (修饰符号 !) 来帮助类型系统识别类型。后面具体介绍它,先看下代码中怎么使用:
class StrictClass {
// …
baz!: boolean;
// ^
// 注意到这个!标志
// 代表着显式赋值断言修饰符
}


显式赋值断言(Definite Assignment Assertions)

尽管我们尝试将类型系统做的更富表现力,但我们知道有时用户比TypeScript更加了解类型

跟上面提到的类属性例子差不多,我们无法在给一个值赋值前使用它,但如果我们已经确定它已经被赋值了,这个时候类型系统就需要我们人的介入

let x: number;
initialize();
console.log(x + x);
//          ~   ~
// Error! Variable 'x' is used before being assigned.
 
function initialize() {
    x = 10;
}

添加 ! 修饰:let x!: number,则可以修复这个问题

我们也可以在表达式中使用!,类似 variable as string和 <string>variable :

let x: number;
initialize();
console.log(x! + x!); //ok
 
function initialize() {
    x = 10;
}

下面是一些小代码记录


关于类的继承


在父级类和子级类中一定要写一个constructor函数,如:

// 父级
class Parent{undefined
    name:string;
    age:number;
    constructor(name:string,age:number){undefined
        this.name = name;
        this.age = age;
    }
}
// 子级
class Web extends Tparent{undefined
     constructor(name:string){undefined
    this.name = name;
             }
 }

这个里面要放父级有的所有变量,这样子级才能拿得到。不然会报错。

标识符

 static是静态内容


访问修饰符

 public是共有内容,默认 例子:public name:string;
 private是私有内容,私有属性无法继承和访问  例子: private name:string;

接口

// 通过关键字interface来声明接口
interface laberValue{undefined
    laber:string;
}
// 指定类型为接口类型
function printLaber(laberValue:laberValue) {undefined
    console.log(laberValue.laber)
}
var myobjs = {laber:'hellos rouse'}
printLaber(myobjs)
// 接口的数组类型

interface StringArray{undefined
    //[index:number]这是下标
    [index:number]:string;
}
var myArray:StringArray;
myArray=["fuck","any","www",""];
console.log(myArray[1]);
// 接口的class的类型,对类的约束


interface ClockInterface{undefined
    currentTime:string;
    // void:不需要返回值
    setTime(d:string):void;
}
class Clock implements ClockInterface{undefined
    currentTime:string;
    setTime(){undefined
        console.log('tag '+this.currentTime);
    }
    constructor(currentTime:string){undefined
        this.currentTime=currentTime;
    }
}

var dsq=new Clock('jacks');
dsq.setTime();
// 接口的继承和混合类型


interface Shape{undefined
    color:string;
}

interface PenStroke{undefined
    penWidth:number;
}

interface Square extends Shape,PenStroke{undefined
    sideLength:number;
}

总结:一个接口可以继承多个不同的接口,在类class继承接口后,要对接口中所含有的属性进行一次重写

泛型

// 泛型<T>,这个T你可以任意指定其他内容,如:<K>
// function Hellopes(num:number):number {undefined
//     return num;
// }

function Hellopes<T>(num:T):T {undefined
        return num;
    }
// 当你使用时需要什么类型就设置什么类型<类型>
var output = Hellopes<string>("hello,jack");
console.log(output)
// 泛型的基本运用


function Hellopll<T>(number:T[]):T[]{undefined
    console.log(number.length)
    return number;
}
var listers:Array<string>=Hellopll<string>(["1","2","3"]);
for(var i=0;i<listers.length;i++){undefined
    console.log(listers[i]);
}

Module

解释:主要是把类,接口都定义在一个module中,方便维护和编写,避免出错

// module,每个类和接口要加export
module Validition{undefined
    export interface StringValidator{undefined
    isAccept(s:string):boolean;
}
    var letterRegxp = /^[A-Za-z]+$/;
    var numberRegxp = /^[0-9]+$/;
    export class letters implements StringValidator{undefined
        // 复写函数
        isAccept(s:string):boolean{undefined
            return letterRegxp.test(s);
        }
    }
    export class ZipCodeVailed implements StringValidator{undefined
    isAccept(s:string):boolean{undefined
        return s.length === 5 && numberRegxp.test(s);
    }
}
}
// 泛型的类型


function Heoo<T>(arg:T):T{undefined
    return arg;
}
// 箭头函数意义(arg:K)的函数返回值类型为K
var myhello:<K>(arg:K)=>K=Heoo;
// 因为它本身自带类型检查,所以上诉代码可以简写为:var myhello=Heoo;
console.log(myhello("heoooooo"));
// 调用时定义类型


interface Hellp{undefined
    <T>(arg:T):T;
}
function myHellp<T>(arg:T):T{undefined
    return arg;
}
var MH:Hellp = myHellp;
// MH后面可以接类型,也可以不接,直接写MH("这是函数")
console.log(MH<string>("这是在调用时定义的泛型"));
// 定义接口时定义类型


interface Hellw<T>{undefined
    (arg:T):T;
}
function eee<T>(arg:T):T{undefined
    return arg;
}
var qeew:Hellw<string>=eee;
console.log(qeew("这是在接口定义了泛型"));
// 泛型类(class)


class HelloNumber<T>{undefined
    zeros!:T;
    add!:(x:T,Y:T) => T;
}
// 类的种类在实例化时用<>指定


var myHelloNumber = new HelloNumber<number>();
myHelloNumber.zeros = 10;
myHelloNumber.add =(x,y)=>x+y
console.log(myHelloNumber.zeros)
console.log(myHelloNumber.add(1,5))