# v3-mini

> 使用 composition-api 写法开发小程序

Latest version **1.0.0-Alpha.2** (published 2020-10-20) · ISC license · 0 weekly downloads

## Install

```sh
npm install v3-mini
pnpm add v3-mini
yarn add v3-mini
bun add v3-mini
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.0-Alpha.2 |
| Published | 2020-10-20 |
| First published | 2020-08-21 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 101.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | w_494917128 |
| Maintainers | w_494917128 |

## Links

- npm: https://www.npmjs.com/package/v3-mini
- Repository: https://gitee.com/w_494917128/v3-mini
- npm.io page: https://npm.io/package/v3-mini

## Recent versions

- 1.0.0-Alpha.2 (latest) — 2020-10-20
- 1.0.0-Alpha.1 — 2020-10-19
- 1.0.0-Alpha.0 — 2020-09-17
- 0.0.5 — 2020-08-26
- 0.0.4 — 2020-08-25
- 0.0.3 — 2020-08-24
- 0.0.1 — 2020-08-21

## README

## 参考 [Vue3.x](https://v3.vuejs.org) ，尝试使用 composition-api 开发小程序

### 下载

> npm install v3-mini --save

> [仓库地址](https://gitee.com/w_494917128/v3-mini)

> [小程序npm使用](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)


### 注意
1. 参考 @vue/reactivity 开发小程序版 composition api ，使用 Proxy 而不用 Object.defineProperty 是因为解决属性添加删除上的问题，但[小程序对的 Proxy 的适应性](https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/js-support.html)并不好
2. 采用 [westore](https://github.com/Tencent/westore) 的 json diff 进行数据对比，减少了 setData 的内容


### 解决什么
1. 使用 composition-api 开发，实现全局状态管理
2. 添加了 reactive, ref, computed, watchEffect 方法
3. 兼容原生小程序的写法，在原写法上新增写法
4. 新的写法，data, methods 皆可在 setup 方法中一起定义返回
5. 钩子函数也可直接在 setup 方法中直接定义


### TODO
1. 自定义组件的生命周期应该是按小程序原写法来还是与 Page 页面统一？
2. setup的方法更新属于直接覆盖，是否需要对某些保护属性进行保护？
3. setup返回的ref/reactive对象监听，采用异步setData的方式，数据改变后只执行一次setData,减少了setData方法的执行率
4. 当页面中存在自定义组件时，是先执行自定义组件的setup后执行页面的setup


### 缺点
1. Deps 依赖不同于 Vue3.x 存储于变量中，该版本存储于闭包中，通过js的机制不再引用时进行垃圾回收，使用不当容易造成内容泄露
2. 采用 Proxy 才能实现 reactive 的数据监听（为了完成 属性添加删除上的问题），但小程序对 Proxy 的适应性并非很好
3. setup返回的对象和方法,是在onLoad期间绑定在组件实例上的,依次不太适用于大量静态内容, 建议提前定义好data
4. Provide / Inject 功能基础，有待提升

### 之后版本更新内容
1. 内存回收机制完善（Deps、event）
2. 自定义组件的 特殊的生命周期 暂未实现


### setup

setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。

- 调用时机
在onLoad/attached时候被执行

- 参数
该函数接收 props 作为其第一个参数, context 第二个参数
```js
VPage({
    ... ,
    setup(props, context) {
        context.event.on('load', () => {});
        context.event.emit('load');
        context.setData({
            age: ref(16)
        })
    }
})
```

1. `props` reactive对象，代理小程序原始 onLoad 的参数 options，可通过 toRefs 函数对其进行分解
2. `context`作为上下文对象, 暴露了一些api
3. `event`是事件通知模块, 包含常用的emit, on, once, off, context注册的事件, 在页面/组件被销毁时也会主动off
4. `setData`是封装后的setData, 支持对`reactive/ref对象`的解析, 其他用法和原生一致
5. `this`指向当前页面/组件实例

---


### 包装对象ref

**`ref`**

ref接受参数并将其包装在具有value属性的对象中，然后将其用于访问或更改反应变量的值

`参数`
1. `val` {Any} 任意值，赋值于ref.value

`返回值` {Object} 返回代理后的ref对象

```js
import { ref } from 'v3-mini'

const count = ref(0)
console.log(count.value) // 0

count.value = 1

console.log(count.value) // 1
```

`在视图层中读取`
当该值被`setup`返回, 将进入data值, 可在模板中被读取到, 会自动解套,无需在模板中额外书写`.value`
```html
<view>{{ count }}</view>
<view bindtap="updateCount">+</view>
```
```js
import { VPage, ref } from 'v3-mini'
VPage({
    setup(props, context) {
        const count = ref(0)
        return {
            count,
            updateCount() {
                count.value += 1
            }
        }
    }
})
```


### 拆解包装对象

**`toRefs`**

对 reactive 对象进行拆解，得到多个 ref 对象

`参数`
1. `reactive` {Reactive} reactive 对象

`返回值` {Object} ref 对象集

```js
import { VPage, reactive, toRefs } from 'v3-mini'
VPage({
    setup(props, context) {
        const person = reactive({ name: 'Jack', age: 25 })
        const { name, age } = toRefs(person)
        return {
            name, age,
            updatePerson() {
                name.value = 'Rose'
                person.age = 18
            }
        }
    }
})
```


### 包装对象reactive

**`reactive`**

`参数`
1. `val` {Object|Array} 创建该对象/数组的代理，监听数据变化

`返回值` {Proxy} 返回代理后的reactive对象

将数据包装成一种可观测的类型，当数据产生变更的时候，监听并执行响应操作

```js
import { reactive, watchEffect } from 'v3-mini'

const person = reactive({ name: 'Jack', age: 25 })
watchEffect(() => console.log(person.name)) // Jack
person.name = 'Rose' // Rose
```

reactive 对象中可使用 ref 对象


### 计算属性

**`computed`**

返回一个 不可手动修改的 ref 对象，收集监听当ref/reactive发送变化时随之更新值

`参数`
1. `setter` {Function} 监听变化的回调, 返回任意值

`返回值` {Object} 返回计算后的ref对象

```js
import { ref, computed } from 'v3-mini'

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2
count.value = 5
console.log(plusOne.value) // 6
```

计算属性总是最少会执行一次,为了第一次赋值


### 监听ref/reactive值更新

**`watchEffect`**
当被监听的ref/reactive对象变化时, 将触发

`参数`
1. `callback` 监听变化的回调

`返回值` {Function} 返回停止监听的方法

```js
import { ref, watchEffect } from 'v3-mini'

const count = ref(1)
const stop = watchEffect(() => console.log(`count: ${count.value}`)) // count: 1
count.value++ // count: 2
stop()
count.value++ // 无输出
```

watchEffect 总是最少会执行一次,为了收集依赖
注意：第一次执行 watchEffect 时并没有生成 stop ，在 callback 中使用 stop 会产生错误

---

### 生命周期函数
可以直接导入 `onXXX` 一族的函数来注册生命周期钩子：
```js
import { VPage, onLoad, onHide, onShow } from 'v3-mini'

VPage({
  setup() {
    onLoad(() => {
      console.log('onLoad!')
    })
    onHide(() => {
      console.log('onHide!')
    })
    onShow(() => {
      console.log('onShow!')
    })
  },
}
```


### VPage使用
```html
<view>name: {{name}}</view>
<view>count1: {{count1}}</view>
<view>count2: {{count2}}</view>
<view>allCount.count1: {{allCount.count1}}</view>
<view>allCount.count2: {{allCount.count2}}</view>
<view>allCount.count3: {{allCount.count3}}</view>
<view bindtap="changeName">changeName</view>
<view bindtap="addCount1">addCount1</view>
<view bindtap="addCount3">addCount3</view>
<view bindtap="delCount3">delCount3</view>
<view bindtap="stopWatch">stopWatch</view>
```
```js
import { VPage, ref, toRefs, reactive, watchEffect, computed, onLoad } from 'v3-mini';

VPage({
    setup(props) {
        let { name } = toRefs(props)
        let count1 = ref(1)
        let count2 = computed(() => count1.value * 2)
        let allCount = reactive({ count1, count2 })

        const stop = watchEffect(() => {
            console.log(`监听allCount.${'count2'}: ${allCount['count2']}`);
        })
        function changeName() {
            name.value = 'Rose'
        }
        function addCount1() {
            count1.value++
        }
        function addCount3() {
            allCount.count3 = (allCount.count3 || 0) + 3
        }
        function delCount3() {
            delete allCount.count3
        }
        function stopWatch() {
            console.log(`停止监听allCount.${'count2'}`);
            stop()
        }

        onLoad(() => {
            console.log('onLoad!')
        })
        return { name, count1, count2, allCount, changeName, addCount1, addCount3, delCount3, stopWatch }
    }
})
```


### VComponent使用
```html
<view>name: {{name}}</view>
<view bindtap="changeName">changeName</view>
```
```html
<myView name="Jack" />
```
```js
import { VComponent, toRefs, attached } from 'v3-mini';

VComponent({
    properties:{
        name: String
    },
    setup(props) {
        let { name } = toRefs(props)

        function changeName() {
            name.value = 'Rose'
        }

        attached(() => {
            console.log('attached!')
        })
        return { name, changeName }
    }
})
```

---
_Source: https://npm.io/package/v3-mini · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
