首页
友链
导航
影视
壁纸
统计
留言板
Search
1
el-upload自定义触发按钮及触发上传前判断
826 阅读
2
vue配置二级目录以及nginx多网站部署
698 阅读
3
el-cascader选择任意一级搭配懒加载使用,单选框radio不会触发懒加载
574 阅读
4
joe主题自定义导航页面
502 阅读
5
js获取昨天今天明天日期
471 阅读
web前端
vue
react
javascript
nuxt
typescript
indexDB数据库
微信小程序
美文欣赏
心情随笔
技术分享
其他
PHP
nodejs
博客api实战项目
typecho
登录
Search
标签搜索
web
vue
node项目实战
js
javascript
typecho
css
vuex
router
nginx
git
element
joe
utils
leaflet
dateFormat
map
date
axios
reg
辰漪
累计撰写
66
篇文章
累计收到
122
条评论
首页
栏目
web前端
vue
react
javascript
nuxt
typescript
indexDB数据库
微信小程序
美文欣赏
心情随笔
技术分享
其他
PHP
nodejs
博客api实战项目
typecho
页面
友链
导航
影视
壁纸
统计
留言板
搜索到
40
篇与
web前端
的结果
2024-04-12
js扁平结构转树状结构
扁平数据结构转换为树状结构{tabs}{tabs-pane label="递归方式"}/** * @description 扁平数据转树状 * @param {Array} arr 扁平数据 * @param {String} id id字段名 * @param {String} pid 父id字段名 * @returns {Array} tree 树状结构 */ function arrayToTree(arr, id, pid) { let tree = [] const lv1 = getLv1(arr) // 1. 先将一级菜单获取到,放到一个数组里边 tree = addSon(lv1) function addSon (lv1) { arr.forEach((item) => { if(!item.children) item.children = [] // 给所有菜单加上children节点 // console.log('执行了'); if (item[pid]) { lv1.forEach((lv1Item) => { if (item[pid] === lv1Item[id]) { if(!lv1Item.children) lv1Item.children = [] lv1Item.children.push(item) const newarr = [] newarr.push(item) return addSon(newarr) } }) } }) return lv1 } function getLv1 (arr) { const lv1 = [] arr.forEach((item) => { if (item[pid] === 0) { lv1.push(item) } }) return lv1 } return tree }{/tabs-pane}{tabs-pane label="id映射引用"}/** * @description 扁平数据转树状 * @param {Array} arr 扁平数据 * @param {String} id id字段名 * @param {String} pid 父id字段名 * @returns {Array} tree 树状结构 */ function arrayToTree(arr = [], id = 'id', pid = 'pid') { const data = JSON.parse(JSON.stringify(arr)) const idMap = new Map() const tree = [] // 转换为id映射 data.forEach(item => idMap.set(item[id], item)) // 遍历数据,找到根节点, 并追加children节点 data.forEach(item => { if (!item[pid]) return tree.push(item) const parent = idMap.get(item[pid]) if (!parent.children) parent.children = [] parent.children.push(item) }) return tree }{/tabs-pane}{/tabs}数据测试const arr = [ { id: 1, label: '系统管理', pid: 0 }, { id: 2, label: '用户管理', pid: 1 }, { id: 3, label: '角色管理', pid: 1 }, { id: 4, label: '菜单管理', pid: 1 }, { id: 5, label: '视频管理', pid: 0 }, { id: 6, label: '视频分类', pid: 5 }, { id: 7, label: '视频标签', pid: 5 }, { id: 8, label: '视频标签-add', pid: 7 }, { id: 9, label: '视频标签-del', pid: 7 }, ] // 递归方式 console.log(arrayToTree(arr, 'id', 'pid')) // id映射引用方式 console.log(arrayToTree(arr))测试输出[ { id: 1, label: '系统管理', pid: 0, children: [ { id: 2, label: '用户管理', pid: 1 }, { id: 3, label: '角色管理', pid: 1 }, { id: 4, label: '菜单管理', pid: 1 }, ], }, { id: 5, label: '视频管理', pid: 0, children: [ { id: 6, label: '视频分类', pid: 5 }, { id: 7, label: '视频标签', pid: 5, children: [ { id: 8, label: '视频标签-add', pid: 7 }, { id: 9, label: '视频标签-del', pid: 7 }, ], }, ], } ]
2024年04月12日
40 阅读
2 评论
2 点赞
2024-03-01
个人js工具函数库
使用方法npm install @cy-boy/cy-utilsbaseStringNumberDomOther
2024年03月01日
51 阅读
0 评论
1 点赞
2024-02-26
Joe主题实现首页大图
功能说明{callout color="#f0ad4e"}Joe主题首页大图效果系统版本:typecho 1.2.1 + joe 7.7.1{/callout}效果如下图代码实现第一步:引入html代码修改文件:index.php{tabs}{tabs-pane label="修改位置"}{/tabs-pane}{tabs-pane label="代码"}隐藏内容,请前往内页查看详情{/tabs-pane}{/tabs}第二步:引入css样式1. 在assets/css下新建文件my.homeimg.css2. 写入以下css代码{tabs}{tabs-pane label="修改位置"}{/tabs-pane}{tabs-pane label="代码"}隐藏内容,请前往内页查看详情{/tabs-pane}{/tabs}第三步:添加后台配置{callout color="#f0ad4e"}后台配置方便快速修改图片||文字内容{/callout}修改文件:functions.php{tabs}{tabs-pane label="修改位置"}{/tabs-pane}{tabs-pane label="代码"}隐藏内容,请前往内页查看详情{/tabs-pane}{/tabs}第四步:修改配置,保存,刷新
2024年02月26日
73 阅读
5 评论
1 点赞
2023-05-25
TypeScript笔记
TS简介ts基于javascript语言构建ts是js的超集,对js语法进行了扩展可以在任何支持js的平台运行ts支持es的新特性,并且还新增了一些新的特性,例如一些api,工具,抽象类啊什么的ts支持js的类型,并且新增了一些其他类型(枚举类...)有着丰富的配置选项ts通过配置可以编译成任意版本的js es6 es3等等TS开发环境下载并安装nodejs使用npm全局安装typescript编译器打开cmd或者powershell(管理员打开)输入 npm i -g typescript测试typescript新建一个文件 hello.ts打开cmd输入以下命令进行编译ts文件tsc hello.ts查看是否编译成功,会多一个hello.js文件TS类型声明类型类型描述类型描述number数字string字符串boolean布尔值(true, false)字面量限制变量的值就是该字面量的值any任意类型unknown类型安全的anyvold空值,没有值(undefined)never没有值,不能是任何值objectjs对象arrayjs数组tuple元组,ts新增类型,固定长度数组enum枚举,ts新增类型 先变量类型声明,再进行赋值let a: number; a = 10; a = 20; // a = 'string' // 代码会报错,不能将类型“string”分配给类型“number” console.log(a);类型声明和赋值同时进行(ts会自动判断变量类型)let c = 10; // 相当于 let c: number = 10 c = 20; c = 'string'; // 代码报错 不能将类型“string”分配给类型“number”函数参数,函数返回值类型声明function sum(a: number, b: number): number{ return a + b; } // const count = sum(10, 20, 30); // 代码报错:应有 2 个参数,但获得 3 个 const count = sum(10, 20); console.log('count: ' + count);字面量类型声明 --- 使用 “|” 连接多个类型let e: 10 | 13; e = 10; e = 13; // e = 12; // 不能将类型“12”分配给类型“10”多类型声明 --- 使用 “|” 连接多个类型let d: number | string; d = 1; d = 'string'; // d = false; // 代码报错:不能将类型“boolean”分配给类型“string | number”any和unknown类型let f: any; // 显式any // let f; // 隐式any ts会自动检测类型 f = 1; f = false; f = 'string'; let g: boolean; g = f // any 类型的值 赋值给 其他类型的变量 不会发生报错 let h: unknown; h = 2; h = 'string'; h = true; // g = h; // 代码报错:不能将类型“unknown”分配给类型“boolean” // 我们自己知道 h 就是一个 boolean类型 ts检测器不知道 我们可以自己判断 if (typeof h === 'boolean') { g = h; // 此时不会报错 } // 或者使用 ts 类型断言 直接告诉解析器 变量的类型 g = h as boolean; g = <boolean> h;void和never类型// void 表示空值,该函数没有返回值或者返回值是undefined function fn(): void{ // return 123 代码报错:不能将类型“number”分配给类型“void” return undefined } // never 表示没有值,永远不会有值,没有终点 function fn1(): never{ throw new Error('报错了!') // 抛出错误 程序终止 }object和array类型// object 类型 let a: object; a = {}; a = function () {}; // 使用对象字面量声明类型 --- 可以指定对象中的属性以及类型 // 属性名后面加?表示该属性是可选的 let b: {name: string, age?: number}; // b = {}; //代码报错:类型“{}”缺少类型“{ name: string; age: 18; }”中的以下属性 b = {name: '张三', age: 18}; // b = {name: '张三', age: 18, a: 1, b: 2} 代码报错:只能指定类型中已经存在的属性 // 如果还想要其他属性,可以使用[propName: string]: any; let c: {name: string, age?: number, [propName: string]: any}; c = {name: '李四', a: 1, b: 2}; // 使用箭头函数的形式,设置函数的结构类型 let f: (a: number, b: number) => number; f = function (a, b) { return a + b; } // f = function(a: string, b) { // 两个a的类型不符合 // return 'ss'; // 代码报错:返回值必须符合上述定义返回值类型 // } // array 类型 --- 两种声明写法 // 1. 类型[] 2. Array<类型> let g: string[]; // 字符串数组 g = ['f', 'g']; // g = ['f', 'g', 1] // 代码报错:不能将类型“number”分配给类型“string” let h: Array<number>; // 数字数组元组类型 --- 固定长度的数组// 元组类型 --- 固定长度的数组 // 语法:[string, string, number, ...] let r: [string, number, boolean]; r = ['ss', 12, true]; // 长度符合,且,每个元素的类型也要符合枚举类型 --- 枚举类// enum枚举类型 // 和不同的js对象本质上没什么区别 // 对于开发者来说,相比于拿值类比较判断,枚举类型更加易懂 // 提高易读性和可维护性 // 语法:enum 枚举名称 {} enum Gender { male, // 默认male = 0 female // 默认female = 1 } // let y: {name: string, gender: 0|1}; // console.log(y.gender === 1); let y: {name: string, gender: Gender}; y = {name: '张三', gender: Gender.male} console.log(y.gender === Gender.male);|表示或者 和 &表示且// |表示或者 和 &表示且 let u: 1 | 2; // 对象,既要满足第一个也要满足第二个,即name和age两个属性都要有 let i: {name: string} & {age: number};类型别名 --- 如果一个类型特别长,可以写别名代替type numType = 1 | 2 | 3 | 4 let o: numType; let p: numType; o = 1; p = 2;TS编译选项配置自动编译ts文件(只能自动编译单个ts文件)tsc index.ts -w // 通过-w指令,监听文件的变化,自动编译通过ts配置文件(tsconfig.json)进行编译项目目录下新建tsconfig.json文件此时可直接使用tsc -w命令,自动编译并监听,项目所有ts文件tsconfig.json常用配置项include --- 包含要编译的文件目录以及文件** 表示任意目录,*表示任意文件写法:"include": ["./src/**/*"] src文件夹下的ts文件都要进行编译exclude --- 排除不需要编译的文件目录和文件默认值:["node_modules", "bower_components", "jspm_packages"]** 表示任意目录,*表示任意文件写法:"exclude": ["./exclude/**/*"] exclude文件夹下的ts文件不需要编译extends --- 继承外部json配置文件写法:"extends": "./config/xxx.json"files --- 小项目,或者编译的文件很少,才会使用到这个选项,单独配置要编译的文件写法:"files: ["./src/index.ts", "./src/home.ts"]"compilerOnSave --- 保存ts文件时,是否进行即时编译,布尔值 默认flasebuildOnSave --- 保存ts文件时,是否对文件进行编译,以及其他文件的构建任务,布尔值 默认falsecompilerOptions --- 编译器选项,如何编译ts文件,编译成什么版本都在这个选项中进行配置target --- 指定要编译成那个版本的js默认值:ES3可接受:"ES3", "ES5", "ES6", "ES2015", "ES2016", "ES2017", "ES2018","ES2019", "ES2020", "ES2021", "ES2022", "ES2023", "ESNext"module --- 指定编译成的js文件使用什么模块化规则默认值:target为es3 |es5,默认则是CommonJS规则,否则默认是 ES6/ES2015规则可接受:"CommonJS", "AMD", "System", "UMD", "ES6", "ES2015","ES2020", "ESNext", "None", "ES2022", "Node16", "NodeNext"lib --- 指定运行环境所需要使用的库默认值:浏览器的一些环境,如:DOM,ES5可接受: "ES5", "ES6", "ES2015", "ES2015.Collection", "ES2015.Core", "ES2015.Generator", "ES2015.Iterable", "ES2015.Promise", "ES2015.Proxy", "ES2015.Reflect", "ES2015.Symbol.WellKnown", 等还有许多写法:"lib: ["ES6", "DOM"]"outDir --- 指定ts文件编译后所在的目录outFile --- 将编译后的代码合并到同一个文件(此时模块化规范得是amd|system),不常用allowJs --- 是否对js文件也进行编译默认值:false,不对js文件进行编译checkJs --- 是否对js文件语法进行检查默认值:false,不对js文件语法进行检查removeComments --- 是否移除注释默认值:false, 不移除注释noEmit --- 不要生成编译文件默认值:false,允许生成编译文件noEmitOnError --- 不生成编译文件,如果文件存在错误的话默认值:false,有错误,照样生成编译后的文件strict --- 严格检查的总开关默认值:false,关闭alwaysStrict --- 启用js严格模式默认值:false, 不开启js严格模式noImplicitAny --- 不允许隐式的any类型默认值:false,允许隐式的any类型noImplicitThis --- 不允许不明确的this默认值:false, 允许不明确的thisstrictNullChecks --- 严格检查空值默认值:false,不检查空值strictBindCallApply --- 严格检查call,apply,bind的参数列表strictFunctionTypes --- 严格检查function函数的类型strictPropertyInitialization --- 严格检查属性是否初始化noFallthroughCasesInSwitch --- 检查switch语句包含正确的breaknoImplicitReturns --- 检查函数没有隐式的返回值noUnusedLocals --- 检查未使用的全局变量noUnusedParameters --- 检查未使用的参数allowUnreachableCode --- 允许不可达代码在webpack中使用ts初始化项目创建项目文件夹npm init -y 生成包管理文件package.json文件下载项目依赖包 # webpack项目 npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D # ts文件编译成js文件 npm i ts-loader typescript -D # 使用bable对js兼容性做处理 npm i babel-loader @babel/core @babel/preset-env core-js -D创建webpack.config.js配置文件 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // 入口文件 entry: path.resolve(__dirname, 'src/main.ts'), output: { path: path.resolve(__dirname, 'dist'), // 输出文件目录 filename: 'bundle.js', // 输出文件名称 environment: { arrowFunction: false, // 关闭webpack打包后的自调用箭头函数 }, clean: true, // 每次打包清空上次目录 }, // webpack-dev-server devServer: { host: '127.0.0.1', port: 9223, // 端口号 open: true }, module: { // 处理ts文件 rules: [ { test: /\.ts$/, // use: "ts-loader", use: [ { loader: "babel-loader", options: { presets: [ ["@babel/preset-env", { "targets": {"ie": "11", "chrome": "58"}, corejs: "3", useBuiltIns: "usage" // 按需加载js垫片 使浏览器支持最新api }] ] } }, "ts-loader" ], exclude: path.resolve(__dirname, 'node_modules') } ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'public/index.html') }) ], optimization: { minimize: true, }, resolve: { extensions: [".js", ".ts"] // 快捷导入 无需写后缀 }, mode: 'production' // development production } 创建tsconfig.json配置文件 { "compilerOptions": { "target": "ES2015", "module": "ES2015", "strict": true } }class 类类的定义class 类名 { // 实例属性 通过实例.属性名调用 属性名: 类型 = 值 属性名: 类型 = 值 // 静态属性 通过类.属性名调用 static 属性名: 类型 = 值 // 只读属性 通过实例.属性名调用 不可修改 readonly 属性名: 类型 = 值 // 只读静态属性 通过类.属性名调用 不可修改 static readonly 属性名: 类型 = 值 // 构造函数 constructor(参数: 类型) { this.属性名 = 参数 }, 方法名() { ... } }类的使用class Dog { // 定义属性 name: string age: number constructor(name: string, age: number) { // 构造函数中进行赋值 this.name = name this.age = age } say() { console.log(this.name + ': 汪汪汪'); } } const dog1 = new Dog('小黑', 2) const dog2 = new Dog('小白', 1.5) console.log(dog1.name , dog1.age); dog1.say() dog2.say()类的继承使用extends关键字实现继承如果要给子类添加新的属性,则必须要在子类构造函数中,使用关键字super类,调用一下父类构造函数,并传递参数,此时才可以在子类添加新的属性class Animal { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } say() { console.log(this.name + '在叫'); } } /* 使用 extends 实现类继承 如果在子类中写了constructor构造函数,必须要调用super()重写调用一下父类构造函数,否则父类构造函数不会生效 */ class Dog extends Animal { state: number constructor(name: string, age: number, state: number) { super(name, age) // 给子类创建新的属性 this.state = state } // 方法重写 say() { console.log(this.name + ': 汪汪汪!'); } } class Cat extends Animal{ // 方法重写 say() { console.log(this.name + ': 喵喵喵!'); } } const dog = new Dog('小黑', 2, 0) const cat = new Cat('小白', 1.5) dog.say() console.log(dog.state); cat.say() 抽象类使用 abstract class类定义的类,就是抽象类抽象类不能用来创建对象,只能被继承抽象类可以创建抽象方法,抽象方法不能有实现子类必须要对抽象方法进行重写(function(){ /** * 抽象类 使用 abstract class类定义 * 抽象类不能用于创建对象,只能用来继承 * 抽象类可以创建抽象方法,抽象方法不能有实现,子类必须要对抽象方法进行重写 */ abstract class Animal { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } abstract say(): void } class Dog extends Animal { // 方法重写 say() { console.log(this.name + ': 汪汪汪!'); } } class Cat extends Animal{ // 方法重写 say() { console.log(this.name + ': 喵喵喵!'); } } const dog = new Dog('小黑', 2, 0) const cat = new Cat('小白', 1.5) dog.say() cat.say() })()类的属性封装类的三种属性修饰符public(默认值):属性可以在类,子类,实例对象中进行修改protected:属性,只能在类,子类中进行修改private: 私有属性,只能在类中进行修改私有属性访问,就需要属性封装,类中提供修改和获取属性的方法,降低容错率,避免随意修改属性,使属性更加的安全 (function() { class Person { name: string; // 类,子类,实例对象中都可以进行修改 protected age: number; // 只能在类,子类中进行修改 private sex: number; // 只能在类中进行修改,私有属性 constructor(name: string, age: number, sex: number){ this.name = name; this.age = age; this.sex = sex; } getSex(){ return this.sex; } setSex(sex: number) { if (sex !== 0 && sex !== 1) return // 可以对数据做一些限制,不能随意修改 this.sex = sex; } } const p1 = new Person('张三', 18, 0); // console.log(p1.age); // 代码报错:只能在类“Person”及其子类中访问 // p1.age = 20; // 代码报错:只能在类“Person”及其子类中访问 p1.name = '李四'; // p1.sex = 10; 属性“sex”为私有属性,只能在类“Person”中访问 console.log(p1.getSex()); p1.setSex(1); console.log('修改后的p1:', p1); })()类的简化写法直接在构造其中声明属性,需要添加修饰符// class A { // name: string; // age: number; // constructor(name: string, age: number){ // this.name = name; // this.age = age; // } // } class A { constructor(public name: string, private age: number){ } get _age(){ return this.age } } console.log(new A('张三', 10)); console.log(new A('张三', 10)._age);interface 接口接口用来定义类的结构接口可以当做类型来使用,可以进行类型声明接口里的方法都是抽象方法接口可以重复声明,相同接口会合并属性和方法接口定义类的时候,使用implements关键字实现接口,类必须满足接口定义的结构(function () { type myType = { // 使用type定义类型,不能重复定义相同的type类,会报错 name: string, age: number } // type myType = { // hobby: Array<string> // } interface myInterface { name: string; age: number; say():void // 抽象方法 } interface myInterface { // 多次声明相同接口不会报错 hobby: string[]; } // interface接口:定义类型声明 const obj: myType = {name: '张三', age: 18} let obj2: myInterface obj2 = { name: '张三', age: 18, say() { console.log('hi~'); }, hobby: ['打游戏'] } // interface接口:定义类结构 class Person implements myInterface { name: string; age: number; hobby: Array<string>; constructor(name: string, age: number, hobby: Array<string>) { this.name = name this.age = age this.hobby = hobby }; say (): void { console.log('hi~'); } } const p = new Person('张三', 18, ['健身']) console.log(p); })() generic泛型当遇到类型不明确,无法确定其类型的时候,此时就可以使用泛型泛型名字可以随便起在函数中 function fn<T>(a: T): T{ return a; } // 多个泛型 function fn2<T, K>(a: T, b: K): T{ console.log(b); return a; } // 1. 直接调用 console.log(fn(1)); // 2. 指定泛型类型 console.log(fn<string>('hello')); // 3. 指定多个泛型 console.log(fn2<number, string>(10, 'hello'));在class类中 class D<T>{ name: T; constructor(name: T){ this.name = name } } console.log(new D<string>('张三').name);泛型范围约束使用interface接口约束extends使泛型继承interface接口 // 约束泛型范围 必须要有一个属性 age interface inter{ age: number } function F<T extends inter>(a: T) { return a } F({age: 10})
2023年05月25日
57 阅读
0 评论
1 点赞
2022-09-28
小程序学习笔记
小程序基本了解小程序与普通网页的区别运行环境不同网页运行在浏览器环境中小程序运行在微信环境中API不同运行环境不同,这也就导致小程序无法调用DOM和BOM的API。但是可以调用微信环境中提供的各种API开发模式不同网页的开发模式:浏览器 + 代码编辑器小程序: 申请小程序开发者账号,安装小程序开发者工具,创建和配置小程序项目小程序项目结构|-- project_02 // 根目录 |-- .eslintrc.js // eslint配置 |-- app.js // 小程序入口文件 |-- app.json // 全局配置文件 |-- app.wxss // 全局样式文件 |-- project.config.json // 项目配置文件 |-- project.private.config.json // 项目私有配置文件 会覆盖 project.config.json相同字段 |-- sitemap.json // 配置页面能否被微信索引 |-- pages // 存放页面的文件夹 | |-- index | | |-- index.js // 页面js文件(存放页面数据,事件处理函数等) | | |-- index.json // 页面配置文件(配置页面窗口外观,表现) | | |-- index.wxml // 页面模板文件 | | |-- index.wxss // 页面样式文件 | |-- logs | |-- logs.js | |-- logs.json | |-- logs.wxml | |-- logs.wxss |-- utils // 存放工具函数的文件夹 |-- util.js小程序配置文件app.json全局页面配置文件,配置页面的窗口,页面的路径等{ "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "Weixin", "navigationBarTextStyle":"black" }, "style": "v2", "sitemapLocation": "sitemap.json" } pages节点 ---> 小程序的页面路径,在此配置会默认生成页面的目录结构window节点 ---> 配置页面的背景色,文字颜色等,全局生效style节点 ---> 定义小程序组件使用的样式版本sitemapLocation节点 --> 指定sitemap.json的位置project.config.json项目的配置文件,用来配置跟项目相关的配置setting节点 ---> 定义编译相关的配置projectname ---> 配置项目的名称appid ---> 配置小程序的appidsitemap.json配置页面是否被微信搜索到。{ "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", "rules": [{ "action": "allow", // 允许索引 disallow 不允许索引 "page": "*" // * 表示所有的页面 }] }在项目配置文件peoject.config.json中的setting节点添加 "checkSiteMap": false, 可以隐藏掉索引情况提示信息页面的 .json文件可以对当前页面的窗口外观样式进行配置覆盖全局的app.json中window节点下的配置小程序代码构成如何创建一个页面直接在app.json里边的pages节点下新增页面的路径,项目会自动创建对应的页面目录在pages文件夹右键,新建文件夹,右键新建的文件夹,新建page,输入页面名后会自动生成页面需要的文件,此时app.json的pages节点也会自动添加这个页面路径修改首页对应的页面只需要将pages节点中的路径位置调整一下顺序,第一个就表示首页的页面wxml和html的区别标签名称不同html(div, span, a, img)wxml(view, text, navigator, image)属性不同<a href="#"><navigator url="/pages/home/home"></navigator>支持类似于vue的模板语法数据绑定列表渲染条件渲染wxss和css的区别新增rpx单位css中需要手动对像素px进行换算,例如:rpx单位rpx则可以在不同尺寸的屏幕上自动进行换算提供了全局样式和局部样式app.wxss全局样式会作用于所有的页面局部页面的 .wxss 样式只针对于当前页面的样式小程序中的js文件app.js是整个小程序的入口文件,通过调用App()函数启动小程序页面的 .js文件是页面的入口文件,通过调用Page()函数创建并运行页面普通的 .js文件普通的功能函数模块,封装一些公用的函数或者属性,供其他页面使用小程序中的组件小程序组件分类视图容器基础内容表单组件导航组件媒体组件map地图组件canvas画布组件开放能力无障碍访问常用的视图容器组件view组件类似于div,用来布局scroll-view组件可以滚动的div,用来实现列表滚动swiper和swiper-item组件轮播图容器(swiper)和里边的每一项轮播图(swiper-item),用来实现轮播图常用的基础内容组件text组件普通的文本组件,类似于spanrich-text富文本组件,支持html字符串转换成wxml结构<text>13213212</text> <rich-text nodes="<h1>12313</h1>"></rich-text>其他常用的组件button组件按钮组件,更丰富的功能,可通过open-type属性调用微信的功能(客服,转发,用户授权等)image组件图片组件,存放图片的容器navigator组件页面导航组件,类似于a小程序中的api事件监听api以on开头,监听某个事件例如:wx.onWindowResize(() => {}) 监听窗口尺寸的变化同步api以Sync结尾,可以直接接受函数返回值例如:wx.setStorageSync('key', 'value') 本地存储异步的api不带Sync的api,需要通过success fail compalete接收例如:wx.request()发送一个网络请求 需要通过success回调函数接收数据小程序语法数据绑定,mustache语法// js Page({ data: { // 在这里进行数据绑定 num: 1, src: 'http://wwjjds.com/1.png' } }) // wxml 在wxml模板中 使用mustache语法进行渲染 双大括号的形式 // 绑定内容 <view>{{ num }}</view> // 绑定属性 <image src="{{ src }}"></image> // 表达式 <view>{{ num > 1 ? '大于1' : '小于等于1' }}</view> // 算术运算 <view>{{ num * 100 }}</view>事件绑定tap点击事件 bindtap 或者bind:tapinput文本框输入事件 bindinput 或者bind:inputchange状态改变时触发 bindchange 或者bind:change事件对象的属性(event)属性类型说明typeString事件类型timeStampInteger页面打开到触发这个事件经过的时间,毫秒数targerObject触发事件的组件的一些属性值集合currentTargerObject当前组件的一些属性值集合detailObject额外的一些信息touchesArray触摸事件,当前停留在屏幕上的触摸点信息数组changedTouchesArray触摸事件,当前变化的触摸点信息数组targettarget指的是事件触发的源头currentTargetcurrentTarget指的是正在触发的那个元素// js Page({ tapHandler(e) { console.log(e, 'event'); } })<!-- wxml --> <view class="mine-container" data-count="{{ 2 }}" bindtap="tapHandler"> <button type="primary" data-num="{{ 1 }}">按钮</button> </view>点击按钮,此时事件向上冒泡,e.target指的就是这个button按钮,即事件触发的源头,可以从e. target . dataset. num 拿到num值。e.currentTarget是正在触发事件的那个元素 view,可以e. currentTarget. dataset. count拿到count值为data进行赋值调用this.setData()函数为data中的属性进行赋值操作 btnClick (e) { console.log(e); // 修改data中的数据 this.setData({ // 要修改的属性 : 修改的值 通过this.data拿到data中的数据 num: this.data.num + 1 }) },事件传参通过在wxml模板上绑定属性,绑定方式为 data-*,在事件对象中通过e. target. dataset对象拿到对应的属性// js Page({ tapHandler(e) { console.log(e, 'event'); console.log(e.target.dataset.num) // button绑定的属性data-num } })<!-- wxml --> <view class="mine-container" data-count="{{ 2 }}" bindtap="tapHandler"> <button type="primary" data-num="{{ 1 }}">按钮</button> </view>input事件bindinput进行绑定 通过e.detail.value拿到最新的值// js Page({ data: { inputValue: '' }, inputChange(e) { console.log(e.detail.value); // 文本框中的值 this.setData({ inputValue: e.detail.value // 实现双向绑定 修改文本框数据 data中的数据也一并修改 }) }, })<!-- wxml --> <input value="{{ inputValue }}" bindinput="inputChange"/>条件渲染wx:ifwx:elifwx:else// js Page({ data: { sex: 1 } })<!-- wxml --> <view wx:if="{{ sex === 1 }}">男</view> <view wx:elif="{{ sex === 0 }}">女</view> <view wx:else> 未知 </view>结合block标签进行条件渲染,block标签不会渲染到页面中,只是起到了包裹的作用<!-- wxml --> <block wx:if="{{ true }}"> <view>view1</view> <view>view2</view> </block> <view wx:if="{{ true }}"> <view>view1</view> <view>view2</view> </view>hidden<view hidden="{{ true }}">hidden隐藏</view>wx:if和hidden的区别hidden 通过display: none进行隐藏,wx:if会创建或者移除该元素列表渲染wx:for默认索引是index,数据项是item通过属性 wx:for-index="" 修改对应的索引变量通过属性 wx:for-item="" 修改对应的数据项变量通过wx:key提高渲染效率,不需要mustache语法,索引index,如果每一项有id的话直接写id属性// js Page({ data: { list: [ {id: 1, name: '苹果'}, {id: 2, name: '草莓'}, {id: 3, name: '鸭梨'} ] } })<!-- wxml --> <text>------------列表渲染------------</text> <view wx:for="{{ list }}" wx:key="id"> <text>名称:{{ item.name }}</text> <view>索引:{{ index }}</view> </view> <text>------------列表渲染 自定义index和item------------</text> <view wx:for="{{ list }}" wx:key="id" wx:for-index="ind" wx:for-item="ite"> <text>名称:{{ ite.name }}</text> <view>索引:{{ ind }}</view> </view>rpx单位适配不同屏幕尺寸rpx实现原理:rpx把所有设备屏幕划分为750份,即宽度为750rpx,在小屏幕上,1rpx宽度较小,在大屏幕上,宽度较大。小程序在运行时,会把rpx单位的样式换成对应的px单位,实现屏幕的适配。设备rpx换算px(屏幕宽度 / 750)px换选rpx(750 / 屏幕宽度)iPhone51rpx = 0.42px1px = 2.34rpxiPhone61rpx = 0.5px1px = 2rpxiPhone6 Plus1rpx = 0.552px1px = 1.81rpxiPhone6 屏幕宽度 375px 物理像素750,rpx等分750物理像素即 1rpx = 1物理像素 = 0.5px一般会采用iPhone6设计稿, rpx是整数小程序全局配置app.json全局配置文件常用配置项节点pages配置小程序的页面window配置页面窗口外观tabBar设置小程序的tabBarstyle小程序组件使用的样式版本号(v2)最新版window节点小程序窗口可以分为三类:navigationBar导航栏区域background背景区域,下拉刷新时可见wxml页面的主体布局区域常用的window属性属性名类型默认值说明navigationBarTitleTextString字符串导航栏标题文本内容navigationBarBackgroundColorHexColor#000000导航栏背景色,16进制颜色navigationBarTextStyleStringwhite导航栏标题颜色,仅支持white/blackbackgroundColorHexColor#ffffff窗口背景色,下拉才会显示backgroundTextStyleStringdark下拉loding的样式,仅支持dark/lightenablePullDownRefreshBooleanfalse是否开启全局下拉刷新onReachBottomDistanceNumber50上拉触底事件距离页面底部的距离,单位pxtabBar节点tabBar两种:底部tabBar,顶部tabBartabBar至少要配置两项,最多五个顶部的yabBar不会显示icon图标,只显示文本tabBar属性属性类型必填默认值说明positionString否bottomtabBar位置 仅支持bottom/topborderStyleString否blacktabBar上边框的颜色 仅支持black/whitecolorHexColor否 tabBar文本,未选中时的颜色selectedColorHexColor否 tabBar文本,选中时的颜色backgroundColorHexColor否 tabBar的背景色listArray是 tabBar的列表,最少两个,最多五个list数组对象pagePathtab对应的页面路径texttab对应的文本iconPathtab对应的图标selectedIconPathtab选中时的图标// // app.json { "pages": [ "pages/mine/mine", "pages/index/index", "pages/logs/logs", "pages/home/home" ], "window": { "backgroundColor": "#f0f0f0", // 下拉窗口背景色 16进制颜色 "backgroundTextStyle": "dark", // 下拉小圆点的样式 dark / light "navigationBarBackgroundColor": "#ff00ff", // 导航栏背景色 16进制颜色 "navigationBarTitleText": "我的小程序", // 导航栏标题文本 "navigationBarTextStyle": "white", // 导航栏标题文本样式 仅支持white/black "enablePullDownRefresh": true, // 是否开启下拉刷新 全局生效 "onReachBottomDistance": 100 // 上拉触底事件,距离底部的距离 100px }, "tabBar": { "position": "bottom", // tab位置 bottom/top "color": "#000", // tab未选中的文本颜色 "selectedColor": "#f12f00", // tab选中的文本颜色 "backgroundColor": "#f0f0f0", // tab背景色 "list": [ // tab页签选项列表 { "pagePath": "pages/mine/mine", // tab对应的页面路径 "text": "mine", // tab对应的文本 "iconPath": "", // tab对应的图标 "selectedIconPath": "" // tab选中时的图标 }, { "pagePath": "pages/index/index", "text": "index", "iconPath": "", "selectedIconPath": "" }, { "pagePath": "pages/logs/logs", "text": "logs", "iconPath": "", "selectedIconPath": "" } ] }, "style": "v2", "sitemapLocation": "sitemap.json" }小程序网络请求小程序网络请求的限制只能请求https类型的接口接口域名必须要添加到信任列表中(微信公众平台)如何去配置合法接口域名打开微信公众平台 --- 开发 --- 开发管理 --- 开发设置 --- 服务器域名使用wx.request()发起网络请求Page({ onLoad(options) { this.getLoop() // 页面加载之后就会调用 this.onLogin() }, // 发送get请求 getLoop() { wx.request({ url: 'https://www.wrz521.top:8080/api/getLoopArt', // 请求地址 method: 'GET', // 请求方法 data: {}, // 传递的数据 success(res) { // 成功后的回调 console.log(res.data, '请求结果'); }, }) }, // 发送post请求 onLogin() { wx.request({ url: 'https://www.wrz521.top:8080/api/login', method: 'POST', data: { username: 'admin', password: '123456789' }, success(res) { console.log(res.data, '请求结果'); } }) }, })跨域和ajax小程序不存在跨域,不是运行在浏览器上的。ajax依赖于浏览器xhr对象,小程序是依赖微信客户端,不能说是ajax请求,只能说网络数据请求小程序页面导航页面导航的两种方式声明式导航使用navigator组件, 点击navigator组件实现页面跳转跳转tabBar页面 需要指定open-type属性为 switchTab跳转到非tabBar页面 需要指定open-type为navigate编程式导航调用小程序的导航api进行跳转跳转tabBar页面 使用wx.switchTab()跳转到非tabBar页面 使用wx.navigatorTo()跳转到tabBar页面声明式导航:<!-- wxml --> <navigator url="/pages/index/index" open-type="switchTab"> 点击 跳转到tab index页面 </navigator>编程式导航:wx.switchTab()Page({ goTabIndex() { wx.switchTab({ url: '/pages/index/index', success(res) { // 成功回调 console.log(res, 'success'); }, fail(err) { // 失败回调 console.log(err, 'fail'); }, complete(res) { // 成功或失败都会调用 console.log(res, 'complete'); } }) } })跳转到非tabBar页面声明式导航:<!-- wxml --> <navigator url="/pages/home/home" open-type="navigate">点击 跳转到非tab home页面</navigator>编程式导航:wx.navigateTo()Page({ goPageHome() { wx.navigateTo({ url: '/pages/home/home', success(res) { // 成功回调 console.log(res, 'success'); }, fail(err) { // 失败回调 console.log(err, 'fail'); }, complete(res) { // 成功或失败都会调用 console.log(res, 'complete'); } }) }, })后退导航声明式导航使用组件时 指定 open-type属性为navigateBack delta属性为 后退的层级,是一个数字编程时导航使用wx.navigateBack()声明式导航<!-- wxml --> <navigator open-type="navigateBack" delta="1">后退</navigator>编程式导航wx.navigateBack()Page({ goPageHome() { wx.navigateBack({ delta: 1, // 返回的层级 success(res) { // 成功回调 console.log(res, 'success'); }, fail(err) { // 失败回调 console.log(err, 'fail'); }, complete(res) { // 成功或失败都会调用 console.log(res, 'complete'); } }) }, })导航传参声明式导航在url的链接中以查询字符串的形式携带url链接和参数之间 ? 隔开多个参数之间 & 隔开编程式导航在url的链接中以查询字符串的形式携带url链接和参数之间 ? 隔开多个参数之间 & 隔开获取携带的参数在onLoad(options)生命周期函数中,options就是携带的参数对象// wxml <navigator url="/pages/home/home?age=18&name=zs" open-type="navigate"> 点击 跳转到非tab home页面 </navigator>// mine.js Page({ goPageHome() { wx.navigateTo({ url: '/pages/home/home?name=zs&age=18', success(res) { console.log(res, 'success'); }, fail(err) { console.log(err, 'fail'); }, complete(res) { console.log(res, 'complete'); } }) }, })// home.js Page({ onLoad(options) { console.log(options) // 页面参数 {name: zs, age: 18} } })小程序页面事件下拉刷新事件onPullDownRefresh()开启下拉刷新app.json 设置 window节点 设置enablePullDownRefresh: true,全局生效页面的json 设置enablePullDownRefresh: true,只有当前这个页面生效(建议)监听页面的onPullDownRefresh事件下拉刷新会触发需要手动调用wx.stopPullDownRefresh()停止下拉刷新 /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { console.log('页面刷新了'); wx.stopPullDownRefresh({ success(res){ console.log(res, '停止下拉刷新了'); } }) },上拉触底事件onReachBottom()设置上拉触底距离app.json window节点配置 onReachBottomDistance: 100 距离底部100px的位置触发上拉触底事件监听页面的onReachBottom事件上拉到指定位置会触发该事件 /** * 页面上拉触底事件的处理函数 */ onReachBottom() { console.log('上拉触底了'); },滑动页面事件onPageScroll(Object object)参数Object objectscrollTop页面在垂直方向已滚动的距离(单位px)点击右上角菜单‘收藏’按钮onAddToFavorites(Object object)参数Object objectwebViewUrl页面如果包含web-view组件时,会返回web-view的url该事件需要 return一个Object,用来自定义收藏内容Objecttitle自定义标题,默认页面标题或者账号名称imageUrl自定义图片,默认页面截图query自定义query字段,默认当前页面的query更多官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html小程序生命周期什么是生命周期一个对象从创建到销毁的过程就是他的生命周期小程序生命周期小程序生命周期可以分为两类:应用生命周期小程序从启动 --> 运行 --> 销毁的过程页面的生命周期小程序每个页面从加载 --> 渲染 --> 销毁的过程其中页面的生命周期范围小,应用生命周期范围大,两者包含关系小程序启动 --> 页面A生命周期 --> 页面B生命周期 --> ... --> 小程序结束生命周期函数生命周期函数会伴随着生命周期,自动依次调用执行。允许我们在某个特定的时间段,做一些操作,例如在页面加载的时候,初始化页面的数据,此时就需要onLoad生命周期函数。应用生命周期函数需要在app.js中调用App(Object object)函数,指定小程序应用的生命周期回调onLaunch当小程序初始化完成时,会触发 onLaunch(全局只触发一次)onShow当小程序启动,或从后台进入前台显示,会触发 onShowonHide当小程序从前台进入后台,会触发 onHideonError当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息onPageNotFound当页面不存在时触发onUnhandledRejection有未处理的promise,reject事件时触发onThemeChange系统主题变化时触发其他可以添加任意的函数或者数据变量,全局使用,通过this进行调用// app.js App({ onLaunch: function () {}, onShow: function (options) {}, onHide: function () {}, onError: function (msg) {}, globalData: { // 可以定义全局的数据或者函数,通过this进行调用 userInfo: null } })页面生命周期函数需要在页面的 .js中调用Page(Object object)函数,指定页面的生命周期回调onLoad页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数onShow页面显示/切入前台时触发onReady页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互onHide页面隐藏/切入后台时触发。如 wx.navigateTo 或底部tab切换到其他页面,小程序切入后台等onUnload页面卸载时触发。如wx.redirectTo或wx.navigateBack到其他页面时// pages/home/home.js Page({ onLoad(options) { console.log(options, '页面参数'); }, onShow() {}, onReady() {}, onHide() {}, onUnload() {}, })更多官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html小程序wxs脚本wxs和js区别:wxs通常用作过滤器wxs有自己的数据类型wxs不支持es6及以上的语法形式let const 解构赋值 箭头函数等wxs遵循CommonJs规范module对象require()函数module.exports对象wxs用法内嵌在wxml中定义wxs暴露出一个函数,在{{ }}语法中调用 wxs脚本必须要有一个属性module指定模块名<view>{{ m1.toUpper(username) }}</view> <wxs module="m1"> module.exports.toUpper = function (str) { return str.toUpperCase() } </wxs>外联wxs脚本定义wxs后缀的脚本文件 如 tools.wxs在wxs中定义函数,并暴露在wxml中引入wxs脚本文件,调用m2里边的函数就行<wxs src="../../utils/tools.wxs" module="m2"></wxs>注意点只能搭配{{ }}语法使用,不能作为事件回调来使用wxs不能调用js中的函数wxs不能调用小程序提供的api小程序自定义组件如何自定义在项目根目录components文件夹中创建文件夹右键文件夹,新建Component,会自动生成组件需要的文件局部注册组件在页面的.json文件的usingComponents 节点中注册注册方式 组件名称:组件路径{ "usingComponents": { "my-test": "/componnets/test/test" } }全局注册组件在app.json文件的usingComponents 节点中注册注册方式同上如何使用在wxml中以标签的形式使用<my-test></my-test>组件特点组件样式隔离不会影响其他组件或者页面只有class类选择器才会受到样式隔离如何修改样式隔离在组件的js文件中添加options节点,添加属性styleIsolation为isolatedComponent({ options: { styleIsolation: "isolated" }, })styleIsolation属性值可选值默认值描述isolated是启用样式隔离,组件内外的class类声明的样式不会相互影响apply-shared否页面的wxss样式会影响到组件内部的样式,组件样式不会影响外部shared否组件内外的样式,相互影响组件和页面的区别组件组件的js调用Component(Object object)函数组件的方法函数定义在Component(Object object)函数的methods节点中其他Component函数的节点页面页面的js调用Page(Object object)函数页面的方法函数直接写在里边就好了其他Page函数的节点// 页面js Page({ data: {}, goBack() { // 页面的方法函数,直接写就完事了 wx.navigateBack({ delta: 1, }) }, onLoad(options) { console.log(options, '页面参数'); console.log('onLoad'); } })// 组件js Component({ options: { styleIsolation: "isolated" }, /* 组件的属性列表 用来接收组件上的属性*/ properties: { num: { type: Number, // 属性类型 value: 10 // 默认值 }, num1: Number, // 简化版 }, /* 组件的初始数据 */ data: {}, /* 组件的方法列表 */ methods: { goBack() { // 组件的方法函数,定义在methods节点 wx.navigateBack({ delta: 1, }) }, } })Component(Object object)函数参数Object objectoptions类型:Object描述:组件配置选项data类型:Object描述:存放组件私有数据properties类型:Object | Map描述:组件外传递过来的属性映射,接收methods类型:Object描述:组件的方法函数更多参数节点参考:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html组件数据监听Components函数添加observers节点监听多个字段 num1, num2监听对象的属性 obj.prop1,obj.prop2监听对象所有属性,使用通配符*,obj.* 代表所有的属性Component({ observers: { // 数据监听器 'num, num2': function (newNum, newNum2) { // 监听多个字段 // 做一些事情 }, 'obj.prop1, obj.prop2': function (newProp1, newProp2) { // 监听对象属性 // 做一些事情 }, 'obj.**': function (newObj) { // 监听对象所有的属性 // 做一些事情 } }, })纯数据字段特点:不会再页面中展示渲染不会传递给其他组件使用有利于提升页面更新的性能设置纯数据字段:options节点新增pureDataPattern属性pureDataPattern值是一个正则表达式符合表达式的数据就是纯数据字段Component({ options: { styleIsolation: "isolated", pureDataPattern: /^_/ // 以下划线开头的数据,就是纯数据字段 }, data: { _a: true } })组件生命周期函数生命周期函数created组件示例刚刚创建完执行不能调用setData函数,只能在this上挂载一些自定义属性字段attached组件初始化完毕,进入到页面节点树执行可以发送网络请求,初始化一些数据ready组件在视图布局完成后执行moved组件实例被移动到节点树的另一个位置时执行detached组件实例被从页面节点树移除时执行使用的两种方法:直接和data平级,写就完事了使用 lifetimes节点,生命周期函数写在这里边(推荐)Component({ lifetimes: { created() { console.log('test1组件 created'); }, attached() { console.log('test1组件 attached'); } } })组件所在页面的生命周期函数生命周期函数show组件所在页面显示的时候触发hide组件坐在页面隐藏的时候触发resize组件所在页面的尺寸发生变化的时候触发使用方法新增pageLifetimes节点,将函数写在里边Component({ lifetimes: { created() { console.log('test1组件 created'); }, attached() { console.log('test1组件 attached'); } }, pageLifetimes: { show() { console.log('组件所在页面显示了 show'); }, hide() { console.log('组件所在页面隐藏了 hide'); }, resize() { console.log('组件所在页面窗口尺寸变化了 resize'); } } }) 组件插槽匿名插槽使用slot标签占位时,不设置name属性具名插槽使用slot标签占位时,设置name属性开启组件多插槽组件默认只能使用一个插槽在options节点,新增multipleSlots: true,开启多插槽支持<!-- 组件wxml --> <view> <slot name="before"></slot> <slot></slot> <slot name="after"></slot> </view> <!-- 引用组件的wxml --> <cy-test1> <view slot="after"> 渲染到name为 after的插槽 </view> <view slot="before"> 渲染到name为 before的插槽 </view> <view>渲染到默认插槽</view> </cy-test1>小程序组件传值父子间组件通讯通讯方法:属性绑定绑定属性在子组件上,子组件内通过properties进行接收事件绑定通过bind绑定自定义事件,子组件内通过this. triggerEvent(自定义事件名称, 传递的数据)进行调用获取组件实例直接通过this.selectComponent()获取组件实例父向子传值通过属性绑定<!-- 父组件 使用组件,并添加属性arrList="父组件的某个数据" --> <my-test arrList="{{ arr }}"> <text slot="before">before对应的插槽内容</text> 1231456 <text slot="after">after对应的插槽内容</text> </my-test> <!-- 在子组件的js的properties节点中添加对应的属性,并声明类型或者默认值 --> properties: { arrList: { type: Array } } <!-- 子组件封装 --> <view style="background: pink;"> <slot name="before"></slot> <slot></slot> <slot name="after"></slot> <view>----------</view> <text wx:for="{{ arrList }}" wx:key=" id "> 接受到了父组件传递的数组: {{ item.name }}, {{ item.id }} // 拿到接收到的数据渲染 </text> </view>子向父传值通过事件绑定// 调用组件 并绑定自定义事件 <my-test arrList="{{ arr }}" fatherNum="{{ num }}" bind:getValue="getValue"> <text slot="before">before对应的插槽内容</text> 1231456 <text slot="after">after对应的插槽内容</text> </my-test> // js getValue (e) { console.log(e, '子组件调用父组件的getValue函数'); }, // 子组件触发这个自定义函数 并传值 this.triggerEvent('getValue', {value: 5})获取组件实例使用this.selectComponent() 传入id或者class选择器this.selectComponent('.my-text1') // 组件标签添加class类behaviors类似于vue mixins混入使用方法:创建behaviors,新建一个js文件, 调用Behavior()函数创建一个behavior实例对象,通过module.exports向外暴露,组件调用时,会被合并到组件中// js module.exports = Behavior({ data: {}, properties: {}, methods: {} })通过require导入该js文件,并且在组件的behaviors节点中注册使用const myBehavior = require('../../behaviors/my-behaviors') // 在组件中添加behaviors节点,对应一个数组 behaviors: [myBehavior],behaviors中可用的节点 同组件属性小程序npm包npm包支持和限制不支持依赖nodejs内置库的npm包不支持依赖浏览器对象的npm包不支持依赖c++插件的npm包下载和使用新版: 下载npm包 --> 工具 --> 构建npm包旧版: 下载npm包 --> 右侧详情模块勾选npm包 --> 工具 --> 构建npm包小程序全局共享容器mobx全局共享小程序可以使用mobx-miniprogram和mobx-miniprogram-bindings实现全局数据共享。mobx-miniprogram用来创建store对象mobx-program-bindings用来挂载store容器到页面 或者 组件中如何使用mobx下载npm包npm install mobx-miniprogram --save npm i mobx-miniprogram-bindings --save # or npm install --save mobx-miniprogram mobx-miniprogram-bindings创建store容器新建store.js文件import { observable, action } from 'mobx-miniprogram' // 创建store容器 export const store = observable({ // 数据字段 numA: 1, numB: 2, // 计算属性 get sum() { return this.numA + this.numB }, // action方法 修改数据 update: action(function(a = 0, b = 0) { this.numA += a this.numB += b }) })在页面中使用// js import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from '../../store/store' Page({ data: {}, editNum(e) { const a = e.target.dataset.a || 0 const b = e.target.dataset.b || 0 this.update(a, b) }, onLoad(options) { // 挂载store容器 this.storeBindings = createStoreBindings(this, { store, // store容器 fields: ['numA', 'numB', 'sum'], // 映射数据字段和计算属性 actions: ['update'] // 映射action方法 }) }, })<!-- wxml --> <view>{{numA }} + {{ numB }} = {{ sum }}</view> <van-button type="primary" bindtap="editNum" data-a="{{ 1 }}">A + 1</van-button> <van-button type="info" bindtap="editNum" data-b="{{ 1 }}">B + 1</van-button>在组件中使用// js import { storeBindingsBehavior } from 'mobx-miniprogram-bindings' import { store } from '../../store/store' Component({ behaviors: [storeBindingsBehavior], // 挂载behavior storeBindings: { // storeBindings配置选项 store, // fields: ['numA', 'numB', 'sum'], fields: { // 可以自定义变量名称 numABC: 'numA' numA: 'numA', // 直接映射numA numB: () => store.numB, // 函数返回numB sum: (store) => store.sum // 函数返回sum }, // actions: ['update'] actions: { update: 'update' } } properties: {}, data: {}, methods: { editNum(e) { const a = e.target.dataset.a || 0 const b = e.target.dataset.b || 0 this.update(a, b) } } }) 小程序分包什么是分包分包指的是将一个完整的小程序项目根据需求划分为不同的子包,在构建时打包成不同的分包,用户在使用的时候按需进行加载分包特点加载规则小程序启动时,默认会下载主包并且启动主包内的页面(tabbar页面需要放到主包)当用户进入分包内的某个页面,客户端会把对应的分包下载下来,进行展示(非tabbar页面按照功能的不同,划分不同的分包,进行按需下载)体积限制整个小程序的所有分包大小不能超过16M(主包 + 所有分包)单个分包或者主包不能超过2M引用原则普通分包可以引用主包内的公共资源不能引用其他分包的私有资源主包不能引用分包的私有资源独立分包资源独立打包原则小程序会按照subPackages的配置进行分包subPackages之外的目录将被打包到主包中tabbar页面必须要在主包中分包之间不能互相嵌套分包好处可以优化小程序首次启动的下载时间,在多团队协作开发时能够更好的解耦协作项目构成分包前小程序所有的页面和资源都被打包在一起整个项目体积过大,影响小程序的首次启动的下载时间分包后小程序项目由一个主包和多个分包组成主包一般只包含项目的启动页面或者tabbar页面,以及所有分包都需要使用的一些公共资源分包只包含和当前分包有关的页面和私有资源如何配置分包在app.json中添加节点 subPackages 是一个数组,多个分包,就写多个对象 "subPackages": [ { "root": "pkgA", // 分包的根目录 "name": "p1", // 分包的别名 "pages": [ // 分包下面的页面 会自动创建 "pages/cat/cat", "pages/logs/logs" ] } ],独立分包独立分包和普通分包区别独立分包本质上还是分包功能独立不能引用主包公共资源,不能引用分包的私有资源,独立分包之间也不可相互引用,资源完全独立不依赖主包,可以单独运行,提升分包页面的启动速度普通分包普通分包依赖于主包,需要先下载主包可以引用主包公共资源配置独立分包只需要在分包中添加independent为true,则当前分包就是一个独立分包{ "root": "pkgB", "name": "p2", "pages": [ "pages/cat/cat", "pages/logs/logs" ], "independent": true }分包预下载分包预下载指的是,在进入小程序的某个页面时,由框架自动预下载可能需要的分包,从而提升后续进入分包时页面的启动速度配置分包预下载预下载分包的行为,会在进入到指定的页面时触发。在app.json中配置preloadRule节点,定义预下载规则。分包预下载所有页面共享2M,预下载的大小不能超过2M "preloadRule": { "pages/logs/logs": { // 页面路径,哪一个页面,进入这个页面,预加载哪些packges分包 "packages": ["p1", "p2"], // 分包的name别名或者 root根目录 "network": "all" // 下载环境 all 所有 wifi 仅限wifi环境 } },
2022年09月28日
87 阅读
0 评论
0 点赞
2022-09-23
vue.draggable可拖动组件使用方法
安装方式npm install -S vuedraggable全局组件注册import Vue from 'vue'; import Draggable from 'vuedraggable' Vue.component('Draggable', Draggable)局部组件注册<script> import Draggable from 'vuedraggable' export default { data() { return {} }, components: { Draggable } } <script>基础使用使用Draggable包裹,使用animation属性设置过渡效果<template> <Draggable v-model="list" animation="300" > <transition-group> <div v-for="item in list" :key="item.id" class="drag-item"> {{ item.name }} </div> </transition-group> </Draggable> </template> <script> export default { data() { return { list: [ { name: 'zs', age: 18, id: 1 }, { name: 'lisi', age: 20, id: 2 }, { name: 'wangwu', age: 19, id: 3 } ] } } } </script>鼠标在指定元素上才允许拖动通过handle定义可拖动元素的样式名称<template> <Draggable v-model="list" animation="300" handle=".move-drag" > <transition-group> <div v-for="item in list" :key="item.id" class="drag-item"> <i class="el-icon-rank move-drag" /> {{ item.name }} </div> </transition-group> </Draggable> </template> <script> export default { data() { return { list: [ { name: 'zs', age: 18, id: 1 }, { name: 'lisi', age: 20, id: 2 }, { name: 'wangwu', age: 19, id: 3 } ] } } } </script>不允许拖动的元素filter属性定义不可拖动元素的样式名称,该元素则不可拖动,通过move事件,禁止其他元素拖动到它这一行<template> <Draggable v-model="list" animation="300" handle=".move-drag" filter=".forbid" :move="filterMove" > <transition-group> <div v-for="item in list" :key="item.id" class="drag-item" :class="{forbid: item.id === 1}" > <i class="el-icon-rank move-drag" /> {{ item.name }} </div> </transition-group> </Draggable> </template> <script> export default { data() { return { list: [ { name: 'zs(不允许停靠和拖拽)', age: 18, id: 1 }, { name: 'lisi', age: 20, id: 2 }, { name: 'wangwu', age: 19, id: 3 } ] } }, methods: { // 禁止拖动到id为1的那一行 filterMove(e) { console.log(e.relatedContext.element.id, '禁止拖动的id') if (e.relatedContext.element.id == 1) return false return true } } } </script>设置选中样式 和 目标位置的样式chosenClass属性用来设置选中时的样式,ghostClass属性用来设置目标位置的样式<template> <Draggable v-model="list" animation="300" handle=".move-drag" filter=".forbid" :move="filterMove" chosen-class="chose" ghost-class="ghost" > <transition-group> <div v-for="item in list" :key="item.id" class="drag-item" :class="{forbid: item.id === 1}" > <i class="el-icon-rank move-drag" /> {{ item.name }} </div> </transition-group> </Draggable> </template> <script> export default { data() { return { list: [ { name: 'zs(不允许停靠和拖拽)', age: 18, id: 1 }, { name: 'lisi', age: 20, id: 2 }, { name: 'wangwu', age: 19, id: 3 } ] } }, methods: { // 禁止拖动到id为1的那一行 filterMove(e) { console.log(e.relatedContext.element.id, '禁止拖动的id') if (e.relatedContext.element.id == 1) return false return true } } } </script> <style lang="scss" scoped> .drag-item { &.chose { background: skyblue; color: #fff; } &.ghost { background: pink; } } </style>
2022年09月23日
212 阅读
0 评论
2 点赞
2022-09-23
el-select下拉树的实现
el-select实现下拉树// template<el-select ref="el-select-ref" v-model="form.pid" @change="selectChange" @visible-change="showSelectTree" @clear="clearSelectTree" > <!-- 添加一个option选项 使用hidden隐藏 value是下拉选中的值,label是显示的名称 --> <el-option key="id" hidden :value="form.pid" :label="form.pidName" /> <el-tree ref="el-select-tree" :data="treeData" :props="defaultProps" node-key="menuId" highlight-current default-expand-all :expand-on-click-node="false" @node-click="handleNodeClick" /> </el-select>// script<script> export default { data() { return { tableData: [{ "menuId": 8, "pid": 0, "name": "系统管理", "children": [ { "menuId": 3, "pid": 8, "name": "用户管理", "children": [ { "menuId": 4, "pid": 3, "name": "用户列表", "children": [] }, { "menuId": 9, "pid": 3, "name": "用户添加", "children": [] }, { "menuId": 10, "pid": 3, "name": "用户编辑", "children": [] }, { "menuId": 11, "pid": 3, "name": "用户删除", "children": [] } ] }, { "menuId": 12, "pid": 8, "name": "角色管理", "children": [ { "menuId": 17, "pid": 12, "name": "角色列表", "children": [] }, { "menuId": 18, "pid": 12, "name": "角添加色", "children": [] }, { "menuId": 19, "pid": 12, "name": "编辑角色", "children": [] }, { "menuId": 20, "pid": 12, "name": "删除角色", "children": [] } ] }, { "menuId": 7, "pid": 8, "name": "字典管理", "children": [] }, { "menuId": 2, "pid": 8, "name": "菜单管理", "children": [ { "menuId": 13, "pid": 2, "name": "菜单列表", "children": [] }, { "menuId": 14, "pid": 2, "name": "添加菜单", "children": [] }, { "menuId": 15, "pid": 2, "name": "编辑菜单", "children": [] }, { "menuId": 16, "pid": 2, "name": "删除菜单", "children": [] } ] }, { "menuId": 21, "pid": 8, "name": "聚合图床", "icon": "tree", "children": [] } ] }], form: { pid: 0, pidName: '根目录' }, defaultProps: { children: 'children', label: 'name' } } }, methods: { showSelectTree(bool) { // el-select下拉框显示或隐藏触发 // 设置当前绑定的tree节点高亮 if (bool) this.$refs['el-select-tree'].setCurrentKey(this.form.pid) }, handleNodeClick(data, node, nodeRef) { // 点击tree下拉节点 // console.log(data, node, nodeRef) this.form.pid = data.menuId // 设置el-select绑定的value this.form.pidName = data.name // 设置el-select绑定的lable this.$refs['el-select-ref'].handleClose() // 关闭el-select下拉框 // this.$set(this.form, 'pid', data.menuId) }, clearSelectTree() { // 清空el-select触发 需要给el-select添加 clearable属性 this.form.pidName = '' this.$refs['el-select-tree'].setCurrentKey(null) }, } } </script>实现效果展示如需封装成组件,可以自行进行封装,这里就不再进行组件封装
2022年09月23日
25 阅读
0 评论
1 点赞
2022-05-26
js防抖和节流
防抖和节流防抖:在给定时间内没有再次触发,该函数才会执行。节流:先执行一次,在给定时间内该函数不会被再次触发。js两种定时器setTimeOut:该定时器n秒后执行一次函数,只会执行一次。setInterVal:该定时器n秒执行一次函数,循环多次执行。call、apply、bind共同点: call、apply、bind都可以改变this的指向,第一个参数都是this的指向。异同点:call 第二个参数及其以后的参数都是想要传递的参数,他传递的是一个参数列表 例如:fn.call(this, 1,2,3),函数会立即执行。apply 第二个参数传递的是一个参数数组 例如:fn.apply(this, [1,2,3]),函数立即执行。bind 第二个参数及其以后的参数都是想要传递的参数,他传递的是一个参数列表 例如:fn.bind(this, 1,2,3), 会返回一个函数,需要接收并且手动调用。防抖函数和节流函数{tabs}{tabs-pane label="防抖"}// 防抖函数 function debounce(fn, wait) { let timer = null // console.log(this, '1'); // this => window return function () { const e = arguments[0] // arguments[0] => input事件对象 // console.log(this, '2'); // this => input timer && clearTimeout(timer) timer = setTimeout(() => { fn.call(this, e) // 调用fn 并传递input事件对象e }, wait); } }{/tabs-pane}{tabs-pane label="节流"}// 节流函数 function throttle(fn, wait) { let timer = null return function() { const args = arguments if (!timer) { fn.apply(this, args) timer = setTimeout(() => { timer && clearTimeout(timer) timer = null }, wait) } } }{/tabs-pane}{/tabs}小案例测试<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>js防抖和节流</title> </head> <body> <input type="text" id="inp1"> <input type="text" id="inp2"> <script> const $ = function (selector) { return document.querySelector(selector) } // 测试防抖-给定时间里内没有再次触发才会执行 $('#inp1').addEventListener('input', debounce(function (e) { console.log(this, '3'); // this => input console.log(e, '事件对象'); console.log(e.target.value) }, 1000)) // 测试节流-先执行一次 再次执行需要等到时间超过给定时间才会执行 $('#inp2').addEventListener('input', throttle(function (e) { console.log(this, '3'); // this => input console.log(e, '事件对象'); console.log(e.target.value) }, 1000)) </script> </body> </html>
2022年05月26日
130 阅读
2 评论
0 点赞
2022-05-12
js截取字符串长度,添加省略号(...)
{callout color="#f0ad4e"}js处理字符串长度,长度超出,使用‘...’代替超出部分{/callout}/** * @description 根据长度截取先使用字符串,追加… * @param {string} str 对象字符串 * @param {string} len 目标字节长度 * @returns {string} 处理好的字符串 */ export function cutString(str, len = 13) { if (!str) return '' //length属性读出来的汉字长度为1 if(str.length*2 <= len) return str var strlen = 0 var s = "" for(var i = 0;i < str.length; i++) { s = s + str.charAt(i) if (str.charCodeAt(i) > 128) { strlen = strlen + 2 if(strlen >= len){ return s.substring(0,s.length-1) + "..." } } else { strlen = strlen + 1 if(strlen >= len){ return s.substring(0,s.length-2) + "..." } } } return s; }
2022年05月12日
240 阅读
2 评论
0 点赞
2022-05-06
el-cascader选择任意一级搭配懒加载使用,单选框radio不会触发懒加载
1. 问题如图单选按钮点击后,没有进行懒加载,直接显示空数据2. 解决给el-cascader绑定change事件,手动调用lazyLoad方法<!-- 选择文件夹 --> <el-dialog title="选择文件夹" :visible.sync="dialogVisible" width="30%" append-to-body > <div class="" v-if="dialogVisible"> <span>文件夹:</span> <el-cascader class="cyupload-cascader" ref="folder-cascader-ref" :props="props" :show-all-levels="false" @change="change" clearable > </el-cascader> </div> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="sureBtn">确 定</el-button> </span> </el-dialog>// 级联状态变化 change (val) { // console.log(val); console.log(this.$refs['folder-cascader-ref'], 'ref'); this.folderId = val[val.length - 1] // this.$refs['folder-cascader-ref'].dropDownVisible = false // 关闭面板 const selectNode = this.$refs['folder-cascader-ref'].getCheckedNodes()[0] // node节点 if (selectNode && !selectNode.loaded) this.$refs['folder-cascader-ref'].$refs['panel'].lazyLoad(selectNode) // 选中加载 },3. 成功效果图
2022年05月06日
574 阅读
0 评论
1 点赞
1
2
...
4