egg.js学习笔记(中)

本文涉及的产品
数据可视化DataV,5个大屏 1个月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: egg.js学习笔记

获取器和修改器


模型层


// app/model/user.js
module.exports = app => {
    const { STRING, INTEGER, DATE } = app.Sequelize;
    const User = app.model.define('user', {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        name: {
            type: STRING(30),
            // 单独字段的getter,查询时都会调用
            // this.getDataValue('name') 获取原始值
            get() {
                const age = this.getDataValue('age');
                return this.getDataValue('name') + '年龄:' + age;
            }
        },
        age: {
            type: INTEGER,
            // 单独字段的setter,新增和更新时调用
            // this.setDataValue('name') 设置原始值
            set(val) {
                this.setDataValue('age', val * 10);
            }
        },
        created_at: DATE,
        updated_at: DATE,
    });
    // 关联用户资料
    User.associate = function(models) {
        app.model.User.hasOne(app.model.Userinfo);
    }
    return User;
};


控制器层


async show() {
    // 根据主键查询
    let user = await this.ctx.model.User.findOne({
        where: {
            id: 3
        }
    });
    // 获取原始值 user.getDataValue('name')
    this.ctx.body = user.getDataValue('name')
}


模型钩子


模型层


module.exports = app => {
    ...
    // 钩子
    // 查询前
    User.beforeFind((user, option) => {
        console.log('查询前');
    });
    // 查询后
    User.afterFind((user, option) => {
        console.log('查询后');
    });
    // 新增前
    User.beforeCreate((user, option) => {
        console.log('新增前');
    });
    // 新增后
    User.afterCreate((user, option) => {
        console.log('新增后');
    });
    // 修改前
    User.beforeUpdate((user, option) => {
        console.log('修改前');
    });
    // 修改后
    User.afterUpdate((user, option) => {
        console.log('修改后'); // 真正修改才会触发,数据相同不会触发
    });
    // 删除前
    User.beforeDestroy((user, option) => {
        console.log('删除前');
    });
    // 删除后
    User.afterDestroy((user, option) => {
        console.log('删除后');
    });
    return User;
};


查询


主键查询


Model.findByPk(1)


查找不存在则创建


方法 findOrCreate 可用于检查数据库中是否已存在某个元素. 如果是这种情况,则该方法将生成相应的实例. 如果元素不存在,将会被创建.


如果是这种情况,则该方法将导致相应的实例. 如果元素不存在,将会被创建.


假设我们有一个空的数据库,一个 User 模型有一个 username 和 job.


User
  .findOrCreate({
        where: {
            username: 'sdepold'
        }, 
        defaults: {
            job: 'Technical Lead JavaScript'
        }
    })
  . then(([user, created]) => {
    console.log(user.get({
      plain: true
    }))
    console.log(created)
    /*
    findOrCreate 返回一个包含已找到或创建的对象的数组,找到或创建的对象和一个布尔值,如果创建一个新对象将为true,否则为false,像这样:
    [ {
        username: 'sdepold',
        job: 'Technical Lead JavaScript',
        id: 1,
        createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
        updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
      },
      true ]
在上面的例子中,第三行的数组将分成2部分,并将它们作为参数传递给回调函数,在这种情况下将它们视为 "user" 和 "created" .(所以“user”将是返回数组的索引0的对象,并且 "created" 将等于 "true".)
    */
  })


代码创建了一个新的实例. 所以当我们已经有一个实例了 …


User.create({ username: 'fnord', job: 'omnomnom' })
  .then(() => User.findOrCreate({
        where: {
            username: 'fnord'
        }, 
        defaults: {
            job: 'something else'
        }
    }))
  .then(([user, created]) => {
    console.log(user.get({
      plain: true
    }))
    console.log(created)
    /*
    在这个例子中,findOrCreate 返回一个如下的数组:
    [ {
        username: 'fnord',
        job: 'omnomnom',
        id: 2,
        createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
        updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
      },
      false
    ]
    由findOrCreate返回的数组通过第三行的数组扩展为两部分,并且这些部分将作为2个参数传递给回调函数,在这种情况下将其视为 "user" 和 "created" .(所以“user”将是返回数组的索引0的对象,并且 "created" 将等于 "false".)
    */
  })


…现有条目将不会更改. 看到第二个用户的 “job”,并且实际上创建操作是假的.


查找并计数


findAndCountAll - 在数据库中搜索多个元素,返回数据和总计数


这是一个方便的方法,它结合了 findAll 和 count(见下文),当处理与分页相关的查询时,这是有用的,你想用 limit 和 offset 检索数据,但也需要知道总数与查询匹配的记录数:


处理程序成功将始终接收具有两个属性的对象:


  • count - 一个整数,总数记录匹配where语句和关联的其它过滤器


  • rows - 一个数组对象,记录在limit和offset范围内匹配where语句和关联的其它过滤器,


Project
  .findAndCountAll({
     where: {
        title: {
          [Op.like]: 'foo%'
        }
     },
     offset: 10,
     limit: 2
  })
  .then(result => {
    console.log(result.count);
    console.log(result.rows);
  });


它支持 include. 只有标记为 required 的 include 将被添加到计数部分:


假设你想查找附有个人资料的所有用户:


User.findAndCountAll({
  include: [
     { model: Profile, required: true}
  ],
  limit: 3
});


因为 Profile 的 include 有 required 设置,这将导致内部连接,并且只有具有 profile 的用户将被计数. 如果我们从 include 中删除required,那么有和没有 profile 的用户都将被计数. 在include中添加一个 where 语句会自动使它成为 required:


User.findAndCountAll({
  include: [
     { model: Profile, where: { active: true }}
  ],
  limit: 3
});


上面的查询只会对具有 active profile 的用户进行计数,因为在将 where 语句添加到 include 时,required 被隐式设置为 true.


传递给 findAndCountAll 的 options 对象与 findAll 相同(如下所述).


查询多个(常用)


// 找到多个条目
Project.findAll().then(projects => {
  // projects 将是所有 Project 实例的数组
})
// 搜索特定属性 - 使用哈希
Project.findAll({ where: { name: 'A Project' } }).then(projects => {
  // projects将是一个具有指定 name 的 Project 实例数组
})
// 在特定范围内进行搜索
Project.findAll({ where: { id: [1,2,3] } }).then(projects => {
  // projects将是一系列具有 id 1,2 或 3 的项目
  // 这实际上是在做一个 IN 查询
})
Project.findAll({
  where: {
    id: {
      [Op.and]: {a: 5},           // 且 (a = 5)
      [Op.or]: [{a: 5}, {a: 6}],  // (a = 5 或 a = 6)
      [Op.gt]: 6,                // id > 6
      [Op.gte]: 6,               // id >= 6
      [Op.lt]: 10,               // id < 10
      [Op.lte]: 10,              // id <= 10
      [Op.ne]: 20,               // id != 20
      [Op.between]: [6, 10],     // 在 6 和 10 之间
      [Op.notBetween]: [11, 15], // 不在 11 和 15 之间
      [Op.in]: [1, 2],           // 在 [1, 2] 之中
      [Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
      [Op.like]: '%hat',         // 包含 '%hat'
      [Op.notLike]: '%hat',       // 不包含 '%hat'
      [Op.iLike]: '%hat',         // 包含 '%hat' (不区分大小写)  (仅限 PG)
      [Op.notILike]: '%hat',      // 不包含 '%hat'  (仅限 PG)
      [Op.overlap]: [1, 2],       // && [1, 2] (PG数组重叠运算符)
      [Op.contains]: [1, 2],      // @> [1, 2] (PG数组包含运算符)
      [Op.contained]: [1, 2],     // <@ [1, 2] (PG数组包含于运算符)
      [Op.any]: [2,3],            // 任何数组[2, 3]::INTEGER (仅限 PG)
    },
    status: {
      [Op.not]: false,           // status 不为 FALSE
    }
  }
})


复合过滤 / OR / NOT 查询


你可以使用多层嵌套的 AND,OR 和 NOT 条件进行一个复合的 where 查询. 为了做到这一点,你可以使用 or , and 或 not 运算符:


Project.findOne({
  where: {
    name: 'a project',
    [Op.or]: [
      { id: [1,2,3] },
      { id: { [Op.gt]: 10 } }
    ]
  }
})
Project.findOne({
  where: {
    name: 'a project',
    id: {
      [Op.or]: [
        [1,2,3],
        { [Op.gt]: 10 }
      ]
    }
  }
})


这两段代码将生成以下内容:


SELECT *
FROM `Projects`
WHERE (
  `Projects`.`name` = 'a project'
   AND (`Projects`.`id` IN (1,2,3) OR `Projects`.`id` > 10)
)
LIMIT 1;


not 示例:


Project.findOne({
  where: {
    name: 'a project',
    [Op.not]: [
      { id: [1,2,3] },
      { array: { [Op.contains]: [3,4,5] } }
    ]
  }
});


将生成:


SELECT *
FROM `Projects`
WHERE (
  `Projects`.`name` = 'a project'
   AND NOT (`Projects`.`id` IN (1,2,3) OR `Projects`.`array` @> ARRAY[3,4,5]::INTEGER[])
)
LIMIT 1;


用限制,偏移,顺序和分组操作数据集


要获取更多相关数据,可以使用限制,偏移,顺序和分组:


// 限制查询的结果
Project.findAll({ limit: 10 })
// 跳过前10个元素
Project.findAll({ offset: 10 })
// 跳过前10个元素,并获取2个
Project.findAll({ offset: 10, limit: 2 })


分组和排序的语法是相同的,所以下面只用一个单独的例子来解释分组,而其余的则是排序. 你下面看到的所有内容也可以对分组进行


Project.findAll({order: [['title', 'DESC']]})
// 生成 ORDER BY title DESC
Project.findAll({group: 'name'})
// 生成 GROUP BY name


请注意,在上述两个示例中,提供的字符串逐字插入到查询中,所以不会转义列名称. 当你向 order / group 提供字符串时,将始终如此. 如果要转义列名,你应该提供一个参数数组,即使你只想通过单个列进行 order / group


something.findOne({
  order: [
    // 将返回 `name`
    ['name'],
    // 将返回 `username` DESC
    ['username', 'DESC'],
    // 将返回 max(`age`)
    sequelize.fn('max', sequelize.col('age')),
    // 将返回 max(`age`) DESC
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],
    // 将返回 otherfunction(`col1`, 12, 'lalala') DESC
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
    // 将返回 otherfunction(awesomefunction(`col`)) DESC,这个嵌套是可以无限的!
    [sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
  ]
})


回顾一下,order / group数组的元素可以是以下内容:


  • String - 将被引用


  • Array - 第一个元素将被引用,第二个将被逐字地追加


  • Object -


raw 将被添加逐字引用


如果未设置 raw,一切都被忽略,查询将失败


  • Sequelize.fn 和 Sequelize.col 返回函数和引用的列名


字段过滤


想要只选择某些属性,可以使用 attributes 选项. 通常是传递一个数组:


Model.findAll({
  attributes: ['foo', 'bar']
});


SELECT foo, bar …


属性可以使用嵌套数组来重命名:


Model.findAll({
  attributes: ['foo', ['bar', 'baz']]
});


SELECT foo, bar AS baz …


也可以使用 sequelize.fn 来进行聚合:


Model.findAll({
  attributes: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']]
});


SELECT COUNT(hats) AS no_hats …


使用聚合功能时,必须给它一个别名,以便能够从模型中访问它. 在上面的例子中,你可以使用 instance.get('no_hats') 获得帽子数量.


有时,如果你只想添加聚合,则列出模型的所有属性可能令人厌烦:


// This is a tiresome way of getting the number of hats...
Model.findAll({
  attributes: ['id', 'foo', 'bar', 'baz', 'quz', [sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']]
});
// This is shorter, and less error prone because it still works if you add / remove attributes
Model.findAll({
  attributes: { include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']] }
});
SELECT id, foo, bar, baz, quz, COUNT(hats) AS no_hats ...


同样,它也可以排除一些指定的表字段:


Model.findAll({
  attributes: { exclude: ['baz'] }
});
SELECT id, foo, bar, quz ...


Where


无论你是通过 findAll/find 或批量 updates/destroys 进行查询,都可以传递一个 where 对象来过滤查询.


where 通常用 attribute:value 键值对获取一个对象,其中 value 可以是匹配等式的数据或其他运算符的键值对象.


也可以通过嵌套 or 和 and 运算符 的集合来生成复杂的 AND/OR 条件.


基础


const Op = Sequelize.Op;
Post.findAll({
  where: {
    authorId: 2
  }
});
// SELECT * FROM post WHERE authorId = 2
Post.findAll({
  where: {
    authorId: 12,
    status: 'active'
  }
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';
Post.findAll({
  where: {
    [Op.or]: [{authorId: 12}, {authorId: 13}]
  }
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
Post.findAll({
  where: {
    authorId: {
      [Op.or]: [12, 13]
    }
  }
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
Post.destroy({
  where: {
    status: 'inactive'
  }
});
// DELETE FROM post WHERE status = 'inactive';
Post.update({
  updatedAt: null,
}, {
  where: {
    deletedAt: {
      [Op.ne]: null
    }
  }
});
// UPDATE post SET updatedAt = null WHERE deletedAt NOT NULL;
Post.findAll({
  where: sequelize.where(sequelize.fn('char_length', sequelize.col('status')), 6)
});
// SELECT * FROM post WHERE char_length(status) = 6;


操作符


Sequelize 可用于创建更复杂比较的符号运算符 -


const Op = Sequelize.Op
[Op.and]: {a: 5}           // 且 (a = 5)
[Op.or]: [{a: 5}, {a: 6}]  // (a = 5 或 a = 6)
[Op.gt]: 6,                // id > 6
[Op.gte]: 6,               // id >= 6
[Op.lt]: 10,               // id < 10
[Op.lte]: 10,              // id <= 10
[Op.ne]: 20,               // id != 20
[Op.eq]: 3,                // = 3
[Op.not]: true,            // 不是 TRUE
[Op.between]: [6, 10],     // 在 6 和 10 之间
[Op.notBetween]: [11, 15], // 不在 11 和 15 之间
[Op.in]: [1, 2],           // 在 [1, 2] 之中
[Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
[Op.like]: '%hat',         // 包含 '%hat'
[Op.notLike]: '%hat'       // 不包含 '%hat'
[Op.iLike]: '%hat'         // 包含 '%hat' (不区分大小写)  (仅限 PG)
[Op.notILike]: '%hat'      // 不包含 '%hat'  (仅限 PG)
[Op.startsWith]: 'hat'     // 类似 'hat%'
[Op.endsWith]: 'hat'       // 类似 '%hat'
[Op.substring]: 'hat'      // 类似 '%hat%'
[Op.regexp]: '^[h|a|t]'    // 匹配正则表达式/~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.notRegexp]: '^[h|a|t]' // 不匹配正则表达式/!~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.iRegexp]: '^[h|a|t]'    // ~* '^[h|a|t]' (仅限 PG)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (仅限 PG)
[Op.like]: { [Op.any]: ['cat', 'hat']} // 包含任何数组['cat', 'hat'] - 同样适用于 iLike 和 notLike
[Op.overlap]: [1, 2]       // && [1, 2] (PG数组重叠运算符)
[Op.contains]: [1, 2]      // @> [1, 2] (PG数组包含运算符)
[Op.contained]: [1, 2]     // <@ [1, 2] (PG数组包含于运算符)
[Op.any]: [2,3]            // 任何数组[2, 3]::INTEGER (仅限PG)
[Op.col]: 'user.organization_id' // = 'user'.'organization_id', 使用数据库语言特定的列标识符, 本例使用 PG


范围选项


所有操作符都支持支持的范围类型查询.


请记住,提供的范围值也可以定义绑定的 inclusion/exclusion.


// 所有上述相等和不相等的操作符加上以下内容:
[Op.contains]: 2           // @> '2'::integer (PG range contains element operator)
[Op.contains]: [1, 2]      // @> [1, 2) (PG range contains range operator)
[Op.contained]: [1, 2]     // <@ [1, 2) (PG range is contained by operator)
[Op.overlap]: [1, 2]       // && [1, 2) (PG range overlap (have points in common) operator)
[Op.adjacent]: [1, 2]      // -|- [1, 2) (PG range is adjacent to operator)
[Op.strictLeft]: [1, 2]    // << [1, 2) (PG range strictly left of operator)
[Op.strictRight]: [1, 2]   // >> [1, 2) (PG range strictly right of operator)
[Op.noExtendRight]: [1, 2] // &< [1, 2) (PG range does not extend to the right of operator)
[Op.noExtendLeft]: [1, 2]  // &> [1, 2) (PG range does not extend to the left of operator)


组合


{
  rank: {
    [Op.or]: {
      [Op.lt]: 1000,
      [Op.eq]: null
    }
  }
}
// rank < 1000 OR rank IS NULL
{
  createdAt: {
    [Op.lt]: new Date(),
    [Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
  }
}
// createdAt < [timestamp] AND createdAt > [timestamp]
{
  [Op.or]: [
    {
      title: {
        [Op.like]: 'Boat%'
      }
    },
    {
      description: {
        [Op.like]: '%boat%'
      }
    }
  ]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'


关系 / 关联


// 找到所有具有至少一个 task 的  project,其中 task.state === project.state
Project.findAll({
    include: [{
        model: Task,
        where: { state: Sequelize.col('project.state') }
    }]
})


分页 / 限制


// 获取10个实例/行
Project.findAll({ limit: 10 })
// 跳过8个实例/行
Project.findAll({ offset: 8 })
// 跳过5个实例,然后取5个
Project.findAll({ offset: 5, limit: 5 })


排序


order 需要一个条目的数组来排序查询或者一个 sequelize 方法.一般来说,你将要使用任一属性的 tuple/array,并确定排序的正反方向.


Subtask.findAll({
  order: [
    // 将转义标题,并根据有效的方向参数列表验证DESC
    ['title', 'DESC'],
    // 将按最大值排序(age)
    sequelize.fn('max', sequelize.col('age')),
    // 将按最大顺序(age) DESC
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],
    // 将按 otherfunction 排序(`col1`, 12, 'lalala') DESC
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
    // 将使用模型名称作为关联的名称排序关联模型的 created_at.
    [Task, 'createdAt', 'DESC'],
    // Will order through an associated model's created_at using the model names as the associations' names.
    [Task, Project, 'createdAt', 'DESC'],
    // 将使用关联的名称由关联模型的created_at排序.
    ['Task', 'createdAt', 'DESC'],
    // Will order by a nested associated model's created_at using the names of the associations.
    ['Task', 'Project', 'createdAt', 'DESC'],
    // Will order by an associated model's created_at using an association object. (优选方法)
    [Subtask.associations.Task, 'createdAt', 'DESC'],
    // Will order by a nested associated model's created_at using association objects. (优选方法)
    [Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
    // Will order by an associated model's created_at using a simple association object.
    [{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
    // 嵌套关联模型的 created_at 简单关联对象排序
    [{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
  ]
  // 将按年龄最大值降序排列
  order: sequelize.literal('max(age) DESC')
  // 按最年龄大值升序排列,当省略排序条件时默认是升序排列
  order: sequelize.fn('max', sequelize.col('age'))
  // 按升序排列是省略排序条件的默认顺序
  order: sequelize.col('age')
  // 将根据方言随机排序 (而不是 fn('RAND') 或 fn('RANDOM'))
  order: sequelize.random()
})


count - 计算数据库中元素的出现次数


还有一种数据库对象计数的方法:


Project.count().then(c => {
  console.log("There are " + c + " projects!")
})
Project.count({ where: {'id': {[Op.gt]: 25}} }).then(c => {
  console.log("There are " + c + " projects with an id greater than 25.")
})


max - 获取特定表中特定属性的最大值


这里是获取属性的最大值的方法:


/*
   我们假设3个具有属性年龄的对象.
   第一个是10岁,
   第二个是5岁,
   第三个是40岁.
*/
Project.max('age').then(max => {
  // 将返回 40
})
Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
  // 将会是 10
})


min - 获取特定表中特定属性的最小值


这里是获取属性的最小值的方法:


/*
   我们假设3个具有属性年龄的对象.
   第一个是10岁,
   第二个是5岁,
   第三个是40岁.
*/
Project.min('age').then(min => {
  // 将返回 5
})
Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
  // 将会是 10
})


sum - 特定属性的值求和


为了计算表的特定列的总和,可以使用“sum”方法.


/*
   我们假设3个具有属性年龄的对象.
   第一个是10岁,
   第二个是5岁,
   第三个是40岁.
*/
Project.sum('age').then(sum => {
  // 将返回 55
})
Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
  // 将会是 50
})


相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
2月前
|
JavaScript 前端开发 API
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
|
1月前
|
JavaScript 前端开发
【干货分享】JavaScript学习笔记分享
【干货分享】JavaScript学习笔记分享
57 0
|
2月前
|
Web App开发 前端开发 JavaScript
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的云的学习笔记系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的云的学习笔记系统附带文章源码部署视频讲解等
40 0
|
6月前
|
移动开发 JavaScript 前端开发
webgl学习笔记3_javascript的HTML DOM
webgl学习笔记3_javascript的HTML DOM
65 0
webgl学习笔记3_javascript的HTML DOM
|
6月前
|
JavaScript 前端开发 Java
webgl学习笔记2_javascript基础快速学习
webgl学习笔记2_javascript基础快速学习
51 0
|
6月前
|
前端开发 JavaScript API
JavaScript学习笔记(一)promise与async
JavaScript学习笔记(一)promise与async
|
6月前
|
存储 JavaScript
【ES6系列第二篇】适用JS初学者的Set对象和Map对象学习笔记
【ES6系列第二篇】适用JS初学者的Set对象和Map对象学习笔记
48 0
|
存储 JavaScript 前端开发
【js】函数概述学习笔记(8)
【js】函数概述学习笔记(8)
41 0
|
存储 JavaScript
【js】数组学习笔记(7-2)
【js】数组学习笔记(7-2)
65 0