Coder Social home page Coder Social logo

mt-blog's People

Contributors

dependabot[bot] avatar txs1992 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mt-blog's Issues

Vue 高德地图优化实践

地图加载优化

地图组件设计

  • slot
  • 子组件如何共享地图实例

覆盖物设计

信息窗体设计

基于 Snabbdom 的 Virtual DOM 运行原理

image

这片文章主要来自个人对 Snabbdom 源码的阅读与理解,如有错误的地方欢迎指正。

Virtual DOM 是什么

简单来说 Virtual DOM 就是一个包含了对真实 DOM 最小描述对象,同时又通过 Diff 比较新旧节点差异,实现最小 DOM 更新的一种技术。

  • VNode 介绍

VNodeVirtual DOM 对于真实 Virtual DOM 的一种描述对象,我们可以将其理解成 AST。与真实 DOM 的最大差异就是它的属性很少,且没有 DOM 操作 API。

下面这张图是打印了 document.body 这个节点对象的属性,有 300+

image

接下来我们再看 Snabbdom 中的 VNode 源码

image

相比真实 DOM VNode 只有简单的六个属性

  • sel:节点名称 + 选择器的组合例如 div#app
  • data:VNode 的数据,其中包含一些 Snabbdom 的模块配置,例如 class 、style 等等
  • children:子 VNode
  • text:文本节点内容
  • elm:当前需要挂着的真实 DOM 节点
  • key:data 中的 key 属性,用于 diff 算法比较

Virtual DOM 是如何更新到真实 DOM 的

Snabbdom 的 VNode 到真实 DOM 更新的动作是通过 init 函数返回的 patch 函数,patch 的第一个参数是 DOM 节点或者 VNode,第二个参数是 VNode。如果第一个参数传入的是个 DOM 节点,patch 函数会在内部将其转换成空 VNode,之后将两个 VNode 进行对比,对比较完的结果进行更新。

Diff 算法如何比价 children 变化的

总结

微信小程序快速踩坑

前言

最近去新公司花了两个星期撸了一个小程序,该文章主要是总结一下小程序开发的基础知识与一些相关流程,能让我们对微信小程序有一个大致的了解,并能快速进入开发。

文件结构

小程序的页面与组件以文件夹为单位进行划分,每个小程序的组件由 WXML、WXSS、JS、JSON 等四个文件组成。

WXSS

WXSS 是小程序对 CSS 的一种封装,用于描述 WXML 的组件样式,和 CSS 语法大致相同,但不支持 SCSS 等预编译语言的语法,例如嵌套,循环等语法。

WXML

WXML 是小程序框架设计的一种标签语言,有些类似 vue 的模板语法。小程序框架提供数据绑定,列表渲染,条件渲染等功能

<!-- index.xwml -->
<!-- 模板渲染 -->
<view>{{ message }}</view>

<!-- 渲染自定义组件 -->
<MyButton>click me</MyButton>

<!-- 条件渲染 -->
<view wx:if="{{ isPage }}">这里是页面</view>
<view wx:elseif="{{ isPage === 2 }}">这里是组件</view>
<view wx:else>这里是view组件</view>

<!-- 循环渲染 -->
<view wx:for="{{ list }}" wx:key="id">
  {{ index }}: {{ item.name }}
</view>

<!-- index.js -->
Page({
  date: {
    list: [{ name: 'mt', id: 1 }, { name: 'tom', id: 2 }],
    message: "hello mt!",
    isPage: false
  }
});

JS

JS 文件用来控制组件或页面的交互逻辑,或者提供通用工具方法。

// components/my-button/my-button.js
// 组件,目录存放于 components 文件夹下。
Component({
  // 组件属性列表
  properties: {
    text: {
      type: String,
      value: ""
    }
  },

  // 组件私有数据
  data: {},

  // 监听器
  observers: {
    text: function() {}
  },

  // 组件方法
  methods: {}
});

// pages/index.js
// 引入其他模块,只支持 commonjs 语法
const { baseApi } = require('./common/constant.js')

Page({
  // 页面初始数据
  data: {
    baseApi: baseApi
  },

  // 自定义处理函数
  handleClick: function() {},

  // 页面的一些生命周期函数
  onXX: function() {}
});

// common/constant.js
// 公共函数或常量
const baseApi = 'https://github.com/txs1992/'

// 只支持 commonjs 语法
module.exports = {
  baseApi: baseApi
}

JSON

JSON 文件用来设置当前文件是组件或页面,以及对页面的窗口配置。component 设置当前文件是否是组件,默认 false,usingComponents 用来注册当前页面的组件。其他相关配置请阅读 小程序配置

{
  "component": true, 
  "usingComponents": {
    "MyButton": "../../components/my-button/my-button"
  }
}

WXS

在使用 Vue 进行开发的时候,我们可以使用计算属性或是函数去处理一些负责的逻辑, 但是小程序的 WXML 中没有计算属性,也不能直接使用函数,不过我们可以使用 WXS 来编写处理函数,WXS 是小程序模板的脚本语言,可以在 WXML 中使用。

WXS 虽然与 JS 语法一致,但也有些差异,例如不能使用 Date 对象,使用 getDate() 函数来获取日期,细节请阅读 WXS 语法

// util.wxs
function sum(a, b) {
  return a + b
}

// index.wxml
<!-- 导入模板函数 -->
<wxs module="util" src="./util.wxs"></wxs>

<view>{{ sum(priceOne, priceTwo) }}</view>

web-view

web-view 是小程序提供的原生组件,用来显示其他网页链接,该组件会自动铺满屏幕,并在容器上层,如果页面中有其他组件会覆盖其他上方。web-view 嵌入的页面可以通过小程序的 SDK API 与小程序进行交互。

// 嵌入的网页
// <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
// 通知小程序跳转到 download 页面
wx.miniProgram.navigateTo({url: '/pages/download?id=123-xwd'})

// download.js
Page({
  onLoad(options) {
    // 在 onLoad 生命周期中可以获取传递的数据。也可以在 web-view 页面中绑定 bindmessage 事件处理函数。 
    console.log(optins.id)
  }
})

获取小程序权限

在小程序的开发中,我们有时候会需要获取小程序权限,例如获取用户信息。新版小程序不能直接通过小程序 API 获取,而是要通过用户点击 button 组件的形式来获取,例如下面案例中的获取用户手机号。这里有一张小程序的登录流程图。

image

// pages/index/index.wxml 
<!-- 小程序的按钮也许可能不满足我们的需求,可以通过 plain 将其设置为透明按钮,在通过样式修改,也可以放入我们自己的按钮组件,open-type 是小程序开放能力。 -->
<button
  plain
  class="collision-btn-wrapper"
  open-type="getPhoneNumber"
  bindgetphonenumber="getPhoneNumber">
  <MyButton>获取手机号</MyButton>
</button>
<button open-type="getUserInfo">获取用户信息</button>
// pages/index/index.js
Page({
  getPhoneNumber: function (options) {
   // 这里获取的是加密后的信息,需要调用后端接口解密
  wx.request({
    url: 'xxx',
    method: 'post',
    data: { detail:  options.detail },
    success: function (res) {
      console.log(res)
    }
  })
  },

  getUserInfo: function(options) {
    // 打印用户信息
    console.log(options.detail)
    wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        wx.request({
          url: "xxx",
          method: "post",
          data: { code: res.code, userInfo: options.detail },
          success: function(res) {
            console.log(res);
          }
        });
      }
    });
  }
})

小程序转发

我们希望转发小程序来获取更多的用户,同时为了让用户主动去分享小程序的链接,我们需要给用户一些奖励,这时我们就需要知道通过邀请链接进入小程序的用户的邀请人信息。

页面通过绑定 onShareAppMessage 来显示右上角的转发按钮,开发的时候只能通过微信场景值设置来获取分享数据,或者使用体验版,开发版由于不能其他人使用所有无法测试该功能。

// pages/share/share.js
// 获取小程序示例
const app = getApp();

Page({
  // 复用 app 的 onShareAppMessage 处理函数
  onShareAppMessage: app.onShareAppMessage
});

// app.js
App({
  onLaunch(appOptions) {
    // 打印分享人 ID ,如果不是通过分享进入,该属性是 undefined 或者 null。
    console.log(appOptions.inviteUserId)
  },

  globalData: {
    // 后端获取
    userId: 12
  },

  onShareAppMessage: function() {
    const userId = this.globalData.userId;
    return {
      // 分享链接标题
      title: "点点手指,瓜分5000亿积分",
      // 通过分享进入小程序的指定页面
      path: `/pages/comics/comics?inviteUserId=${userId}`,
      // 分享的图片
      imageUrl: "../../assets/share.png"
    };
  }
})

使用 Json Server 搭建本地 Mock 服务

在项目的开发中,我们时常会遇到前后端并行,或是后端开发延后的情况,我们有时需要在后端接口没有开发完之前提前页面部分的开发,而我们很多功能是需要接口的数据来走通链路的,所以我们需要一个 mock 服务来模拟接口。下面是我在开发中使用的一种 mock 服务工具 json server 的一些经验。

开启一个服务

使用 json-server 开启一个服务非常简单,只需要写一个 json 文件,然后执行 json-server —watch xx.json 就可以开启一个本地的数据服务了。默认端口是 3000, localhost:3000

// data.json
{
    "login": {
        "name": "MT",
        "age": 26
    }
}
 
 
// 使用
json-server data.json
// watch 模式
json-server --watch data.json

精确匹配

json-server 支持过滤数据功能,但只支持数组格式数据,例如下面的 JSON 数据,可以使用 userList?id=1 来筛选数据。

{
    "userList": [
        {
            "id": 1,
            "name": "MT",
            "age": 26
        },
        {
            "id": 2,
            "name": "傻馒",
            "age": 24
        },
        {
            "id": 3,
            "name": "小德",
            "age": 26
        }
    ]
}
 
 
// localhost:3000/userList?id=1
// 得出结果
{
    "id": 1,
    "name": "MT",
    "age": 26
}

分页与区间

json-server 支持分页功能,使用 _page=1 来获取第几页数据,默认10条数据一页。可以使用 _limit=5 修改每页数据量。配合 _page=1&_limit=3 完成自定义分页功能。使用 _start 和 _end 来获取区间数据,_start 也可以配合 _limit 一起使用。

{
    "cities": [
        {
            "id": 1,
            "cityName": "北京"
        },
        {
            "id": 2,
            "cityName": "上海"
        },
 
        {
            "id": 3,
            "cityName": "天津"
        },
        {
            "id": 4,
            "cityName": "广州"
        },
        {
            "id": 5,
            "cityName": "深圳"
        },
        {
            "id": 6,
            "cityName": "合肥"
        }
    ]
}

// 获取第二页数据,没页3条数据。
localhost:3000/cities?_page=2&_limit=3
// 获取第 2 到第 5 条数据。
localhost:3000/cities?_start=2&_end=5

排序

json-server 使用 _sort 属性表示对那个字段排序,_order=asc/desc 来进行升序或降序。可以配合分页功能一起使用。

{
    "cities": [
        {
            "id": 1,
            "cityName": "北京"
        },
        {
            "id": 2,
            "cityName": "上海"
        },
 
        {
            "id": 3,
            "cityName": "天津"
        },
        {
            "id": 4,
            "cityName": "广州"
        },
        {
            "id": 5,
            "cityName": "深圳"
        },
        {
            "id": 6,
            "cityName": "合肥"
        }
    ]
}

 // 对 id 进行降序
localhost:3000/cities?_sort=id&_order=desc

条件过滤

_gte 表示大于等于,_lte 小于等于,_ne 不等于, _like 包含,使用方法是需要过滤的属性加上匹配符号,例如我要筛选年龄大于10小于20的,age_gte=10&age_lte=20

{
    "cities": [
        {
            "id": 1,
            "cityName": "北京"
        },
        {
            "id": 2,
            "cityName": "上海"
        },
 
        {
            "id": 3,
            "cityName": "天津"
        },
        {
            "id": 4,
            "cityName": "广州"
        },
        {
            "id": 5,
            "cityName": "深圳"
        },
        {
            "id": 6,
            "cityName": "合肥"
        }
    ]
}
 
// 筛选出 id 大于 2 并且小于 5 的城市。
localhost:3000/cities?id_gte=3&id_lte=4
 
 
// 筛选 id 大于等于 2 的城市。
localhost:3000/cities?id_get=2
 
 
// 筛选 id 不是 3 的所有城市。
localhost:3000/cities?id_ne=3
 
 
// 筛选出 cityName 包含‘上’字的城市。
localhost:3000/cities?cityName_like=上

搜索与关系

属性 q 匹配所有包含的属性,例如我要过滤所有字段包含州的数据,?q=州。_embed 查找数据中所对应的子数据,如果没有默认空数组。_expand 返回数据对应的父数据,需要注意的是如果你直接查询时例如/provinces?_embed=cities/,如果你的子数组中有一个没有包含父 ID,会导致报错。

{
    "cities": [
        {
            "id": 1,
            "cityName": "广州",
            "provinceId": 2
        },
        {
            "id": 2,
            "cityName": "深圳",
            "provinceId": 2
        },
        {
            "id": 3,
            "cityName": "合肥",
            "provinceId": 1
        }
    ],
    "provinces": [
        {
            "id": 1,
            "provinceName": "安徽"
        },
        {
            "id": 2,
            "provinceName": "广东"
 
        }
    ]
}
 
 
// 查询包含'深圳'字段的数据
localhost:3000/?q=深圳
 
 
// 查询所有省,以及它所包含的市。
localhost:3000/provinces?_embed=cities
 
 
// 查询所有市,以及它所包属于的省。
localhost:3000/cities?_expand=province

路由

使用外部服务,可以使用其他服务器的文件,例如 json-server github/taoxusheng/json/db.json。生产随机数据,使用 js 代码编写。使用 —routes 命令开着自定义路由。

/api/posts # → /posts
/api/posts/1  # → /posts/1
/posts/1/show # → /posts/1
/posts/javascript # → /posts?category=javascript
/articles?id=1 # → /posts/1

typescript 踩坑

TS 2307 找不到模块

下面的这段代码,文件放在 packages 目录中,不在 src 目录下。

// packages/noop.ts
export default function noop () {}

// packages/index.ts
// 这里报错了,cannot find module './noop'
import noop from './noop'

解决方法,你需要查看 tsconfig.json 中 include 是否包含了该文件,以及 ts 的模块机制。

{
	"compilerOptions": {
	  "moduleResolution": "node"
	},
	"include": ["packages/**/*.ts"]
}

拉钩 snabbdom 学习作业

答案:

1:b,c,d。

2:d

3:流程如下

  1. 执行 prepatch 与 update 沟子函数
  2. 如果新 VNode 不是 Text 节点:
    • 如果新 VNode 与旧 VNode 都包含了子 VNode 则执行 updateChildren
    • 如果只有新 VNode 包含了子 VNode,先判断旧 VNode 是否有文本,如果有将节点中的文本清空,执行 addVnodes 将子 VNode 插入到 DOM 中。
    • 如果只有旧的 VNode 包含了子 VNode 就将 DOM 中的子 VNode 删除
    • 如果旧的 VNode 有文本,则将文本清空。
  3. 如果旧的 VNode 的文本与新 VNode 的文本不同,这里 VNode
    • 如果旧的 VNode 有子节点,就执行 removeVnodes 删除子节点
    • 将旧的 DOM 节点的文本替换成新 VNode 的文本
  4. 执行 postpatch 沟子函数

选择题

1、下面关于虚拟 DOM 的说法正确的是:
A. 使用虚拟 DOM 不需要手动操作 DOM,可以极大的提高程序的性能。

B. 使用虚拟 DOM 不需要手动操作 DOM,可以极大的提高开发效率。

C. 虚拟 DOM 可以维护程序的状态,通过对比两次状态的差异更新真实 DOM。

D. 虚拟 DOM 本质上是 JavaScript 对象,可以跨平台,例如服务器渲染、Weex 开发等。

2、下面关于 Snabbdom 库的描述错误的是:
A. Snabbdom 库是一个高效的虚拟 DOM 库,Vue.js 的虚拟 DOM 借鉴了 Snabbdom 库。

B. 使用 h() 函数创建 VNode 对象,描述真实 DOM 结构。

C. Snabbdom 库本身可以处理 DOM 的属性、事件、样式等操作。

D. 使用 patch(oldVnode, null) 可以清空页面元素

简答题
1、请简述 patchVnode 函数的执行过程。

使用 introJs 创建引导页

前段时间接到了产品的新需求,让我给雷达系统做引导页,大约有 5 个页面,而且有部分页面是存在跨组件的情况。由于之前从未接触过这种需求,所以一边在网上搜索相关资料,一边和小组成员交流。

3.)
在完成引导页功能后,我总结了三种可以实现引导页的方式。使用图片遮罩层,copy DOM, 使用第三方引导库。

关于 copy DOM 这个我和万俊讨论过,万俊说他们公司之前做引导页,就是将 DOM copy 一份,然后去掉里面的事件,然后定位到元素在浏览器中的位置。因为时间的原因这个方式我并没有去试验,大家有兴趣的可以去试验一下。

接下来说说用图片遮罩层的方式来实现引导页的功能,这种方式看起来很简单,只需要使用几张简单的图片就能完成所有引导功能,但细细一想使用图片遮罩层存在着很多缺陷。

  1. 浏览器窗口适配问题,我可能需要准备多倍图,同时花大量时间来写一堆图片与浏览器窗口适配的样式代码。

  2. 修改起来很麻烦,例如我可能只是简单改几个字,可能就需要重新做好几张图片。

  3. 不能完美匹配 DOM 元素,例如用户修改了浏览器大小,这里的图表明显比原来的要大一些。

使用图片方式存在很多问题,copy DOM 的方式还没有验证,所以我决定在网上找一下,看有没有引导页的工具。最后找到了两个 JS 库。一个是基于 jquery 的 jquery-pagewalkthrough 插件,另一个是 introJS 库,但我最终选择了 introJS。

jQuery Page Walkthrough 虽然可以实现引导页功能,但是它存在一些问题。

  1. 首先它已经很久没有维护了,已经 4 年以上。

  2. 它是 jquery 的插件,所以必须引入 jquery 库。

  3. 另外这个仓库还有问题,当目标节点处于浏览器最右侧,它会在目标节点右边插入遮罩层,导致页面出现滚动条。

  4. 不能修改按钮文本,而且没有 CDN 与 NPM 下载地址。这里有个案例的链接 http://www.jq22.com/demo/jquery-pagewalkthrough-master20160422/

简单介绍一下,introJs 是一个免费的开源库,不过你也可以使用付费版本,他们会帮你做一些定制化的服务。然后说下与 jquery page walkthrough 的区别:

1.首先它不依赖其他第三方工具;

  1. 它有丰富的事件与配置,使得我们可以定制一下复杂的功能。

  2. 文档很全面;

4.但是它还是有点小瑕疵,不支持动态插入新的引导。

这两块代码就是 introJs 的调用方式,左边的是通过 HTML 属性的方式加入引导页功能,右边的是通过 js API 调用的方式来实现。

不过这里有个小细节需要注意一下,当使用 JS API 的方式传入引导项时,HTML 属性的方式将会失效。

HTML 使用示例
JS 使用示例

9)
在完成引导页的需求之后,我很好奇 introJs 是如何做出镂空效果的,然后看了一下它的 CSS 源码,非常的简单通过定位加层级的方式实现,我这里做了一个最简单的实践。

intro-elemnt 是被引导的元素, mask 是遮罩层,layer是用来突出被引导元素的背景。tooltip 是文本提示区。接下来我们来看看 CSS 的代码。

10)
我们可以看到, intro-element 这个被引导的元素,它在整个 DOM 中层级最高,然后 mask 这个遮罩层的元素是最低的。接下来 layer 和 tootip 层级是一样的,它们比 intro-element 稍微要低一些。

这里有个在线效果,我们来看下。https://jsfiddle.net/_MT_/pcwudrmc/62469/

11)
接下来说下在 Vue 中遇到的问题,这张图片就包含了我遇到的两个问题;

  1. 一个是当调用 start 开始引导的时候,DOM 尚未加载成功,就会出现文本提示出现在中间,而引导元素看不到的问题。

  2. 遮罩层突然变得太黑,整个页面除了被引导元素和文本提示元素,其他什么都看不到。我查看了一下 DOM 是因为遮罩层多出了一个,而出现这种问题是在 Vue 中缓存页面导致的,然后将缓存去掉就不会出现这个 BUG。更具体的原因可能需要看 Vue 的缓存源码和 introJs 的源码,有时间再去研究一下。

如何将网站页面当图片下载

const script = document.createElement('script')
script.src = 'https://html2canvas.hertzen.com/dist/html2canvas.js'
document.body.appendChild(script)

function downLoadImage(canvas,name) {
    var a = document.createElement("a")
    a.href = canvas.toDataURL()
    a.download = name
    a.click()
}

html2canvas(document.querySelector('body')).then(canvas => {
  downLoadImage(canvas, 'canvas')
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.