Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?

1. 前言

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以加我微信 ruochuan12,长期交流学习。

之前写的《学习源码整体架构系列》 包含jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4十篇源码文章。

写相对很难的源码,耗费了自己的时间和精力,也没收获多少阅读点赞,其实是一件挺受打击的事情。从阅读量和读者受益方面来看,不能促进作者持续输出文章。

所以转变思路,写一些相对通俗易懂的文章。其实源码也不是想象的那么难,至少有很多看得懂

最近尤雨溪发布了3.2版本。小版本已经是3.2.4了。本文来学习下尤大是怎么发布vuejs的,学习源码为自己所用。

本文涉及到的 vue-next/scripts/release.js文件,整个文件代码行数虽然只有 200 余行,但非常值得我们学习。

歌德曾说:读一本好书,就是在和高尚的人谈话。同理可得:读源码,也算是和作者的一种学习交流的方式。

阅读本文,你将学到:

1. 熟悉 vuejs 发布流程
2. 学会调试 nodejs 代码
3. 动手优化公司项目发布流程

环境准备之前,我们先预览下vuejs的发布流程。

vue 发布流程

2. 环境准备

打开 vue-next[1], 开源项目一般都能在 README.md 或者 .github/contributing.md[2] 找到贡献指南。

而贡献指南写了很多关于参与项目开发的信息。比如怎么跑起来,项目目录结构是怎样的。怎么投入开发,需要哪些知识储备等。

你需要确保 Node.js[3] 版本是 10+, 而且 yarn 的版本是 1.x Yarn 1.x[4]

你安装的 Node.js 版本很可能是低于 10。最简单的办法就是去官网重新安装。也可以使用 nvm等管理Node.js版本。

node -v
# v14.16.0
# 全局安装 yarn
# 克隆项目
git clone https://github.com/vuejs/vue-next.git
cd vue-next# 或者克隆我的项目
git clone https://github.com/lxchuan12/vue-next-analysis.git
cd vue-next-analysis/vue-next# 安装 yarn
npm install --global yarn
# 安装依赖
yarn # install the dependencies of the project
# yarn release

2.1 严格校验使用 yarn 安装依赖

接着我们来看下 vue-next/package.json 文件。

// vue-next/package.json
{"private": true,"version": "3.2.4","workspaces": ["packages/*"],"scripts": {// --dry 参数是我加的,如果你是调试 代码也建议加// 不执行测试和编译 、不执行 推送git等操作// 也就是说空跑,只是打印,后文再详细讲述"release": "node scripts/release.js --dry","preinstall": "node ./scripts/checkYarn.js",}
}

如果你尝试使用 npm 安装依赖,应该是会报错的。为啥会报错呢。因为 package.json 有个前置 preinstall  node ./scripts/checkYarn.js 判断强制要求是使用yarn安装。

scripts/checkYarn.js文件如下,也就是在process.env环境变量中找执行路径npm_execpath,如果不是yarn就输出警告,且进程结束。

// scripts/checkYarn.js
if (!/yarn\.js$/.test(process.env.npm_execpath || '')) {console.warn('\u001b[33mThis repository requires Yarn 1.x for scripts to work properly.\u001b[39m\n')process.exit(1)
}

如果你想忽略这个前置的钩子判断,可以使用yarn --ignore-scripts 命令。也有后置的钩子post。更多详细的可以查看 npm 文档[5]

2.2 调试  vue-next/scripts/release.js 文件

接着我们来学习如何调试 vue-next/scripts/release.js文件。

这里声明下我的 VSCode 版本 是 1.59.0 应该 1.50.0 起就可以按以下步骤调试了。

code -v
# 1.59.0

找到 vue-next/package.json 文件打开,然后在 scripts 上方,会有debug(调试)按钮,点击后,选择 release。即可进入调试模式。

debugger

这时终端会如下图所示,有 Debugger attached. 输出。这时放张图。

terminal

更多 nodejs 调试相关  可以查看官方文档[6]

学会调试后,先大致走一遍流程,在关键地方多打上几个断点多走几遍,就能猜测到源码意图了。

3 文件开头的一些依赖引入和函数声明

我们可以跟着断点来,先看文件开头的一些依赖引入和函数声明

3.1 第一部分

// vue-next/scripts/release.js
const args = require('minimist')(process.argv.slice(2))
// 文件模块
const fs = require('fs')
// 路径
const path = require('path')
// 控制台
const chalk = require('chalk')
const semver = require('semver')
const currentVersion = require('../package.json').version
const { prompt } = require('enquirer')// 执行子进程命令   简单说 就是在终端命令行执行 命令
const execa = require('execa')

通过依赖,我们可以在 node_modules 找到对应安装的依赖。也可以找到其READMEgithub仓库。

3.1.1 minimist  命令行参数解析

minimist[7]

简单说,这个库,就是解析命令行参数的。看例子,我们比较容易看懂传参和解析结果。

$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo', 'bar', 'baz' ],x: 3,y: 4,n: 5,a: true,b: true,c: true,beep: 'boop' }
const args = require('minimist')(process.argv.slice(2))

其中process.argv的第一和第二个元素是Node可执行文件和被执行JavaScript文件的完全限定的文件系统路径,无论你是否这样输入他们。

3.1.2 chalk 终端多色彩输出

chalk[8]

简单说,这个是用于终端显示多色彩输出。

3.1.3 semver  语义化版本

semver[9]

语义化版本的nodejs实现,用于版本校验比较等。关于语义化版本可以看这个语义化版本 2.0.0 文档[10]

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
主版本号:当你做了不兼容的 API 修改,
次版本号:当你做了向下兼容的功能性新增,
修订号:当你做了向下兼容的问题修正。
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

3.1.4 enquirer 交互式询问 CLI

简单说就是交互式询问用户输入。

enquirer[11]

3.1.5 execa 执行命令

简单说就是执行命令的,类似我们自己在终端输入命令,比如 echo 若川

execa[12]

// 例子
const execa = require('execa');(async () => {const {stdout} = await execa('echo', ['unicorns']);console.log(stdout);//=> 'unicorns'
})();

看完了第一部分,接着我们来看第二部分。

3.2 第二部分

// vue-next/scripts/release.js// 对应 yarn run release --preid=beta
// beta
const preId =args.preid ||(semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
// 对应 yarn run release --dry
// true
const isDryRun = args.dry
// 对应 yarn run release --skipTests
// true 跳过测试
const skipTests = args.skipTests
// 对应 yarn run release --skipBuild 
// true
const skipBuild = args.skipBuild// 读取 packages 文件夹,过滤掉 不是 .ts文件 结尾 并且不是 . 开头的文件夹
const packages = fs.readdirSync(path.resolve(__dirname, '../packages')).filter(p => !p.endsWith('.ts') && !p.startsWith('.'))

第二部分相对简单,继续看第三部分。

3.3 第三部分

// vue-next/scripts/release.js// 跳过的包
const skippedPackages = []// 版本递增
const versionIncrements = ['patch','minor','major',...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
]const inc = i => semver.inc(currentVersion, i, preId)

这一块可能不是很好理解。inc是生成一个版本。更多可以查看semver文档[13]

semver.inc('3.2.4', 'prerelease', 'beta')
// 3.2.5-beta.0

3.4 第四部分

第四部分声明了一些执行脚本函数等

// vue-next/scripts/release.js// 获取 bin 命令
const bin = name => path.resolve(__dirname, '../node_modules/.bin/' + name)
const run = (bin, args, opts = {}) =>execa(bin, args, { stdio: 'inherit', ...opts })
const dryRun = (bin, args, opts = {}) =>console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
const runIfNotDry = isDryRun ? dryRun : run// 获取包的路径
const getPkgRoot = pkg => path.resolve(__dirname, '../packages/' + pkg)// 控制台输出
const step = msg => console.log(chalk.cyan(msg))

3.4.1 bin 函数

获取 node_modules/.bin/ 目录下的命令,整个文件就用了一次。

bin('jest')

相当于在命令终端,项目根目录 运行 ./node_modules/.bin/jest 命令。

3.4.2 run、dryRun、runIfNotDry

const run = (bin, args, opts = {}) =>execa(bin, args, { stdio: 'inherit', ...opts })
const dryRun = (bin, args, opts = {}) =>console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
const runIfNotDry = isDryRun ? dryRun : run

run 真实在终端跑命令,比如 yarn build --release

dryRun 则是不跑,只是 console.log(); 打印 'yarn build --release'

runIfNotDry 如果不是空跑就执行命令。isDryRun 参数是通过控制台输入的。yarn run release --dry这样就是truerunIfNotDry就是只是打印,不执行命令。这样设计的好处在于,可以有时不想直接提交,要先看看执行命令的结果。不得不说,尤大就是会玩。

main 函数末尾,也可以看到类似的提示。可以用git diff先看看文件修改。

if (isDryRun) {console.log(`\nDry run finished - run git diff to see package changes.`)
}

看完了文件开头的一些依赖引入和函数声明等,我们接着来看main主入口函数。

4 main 主流程

第4节,主要都是main 函数拆解分析。

4.1 流程梳理 main 函数

const chalk = require('chalk')
const step = msg => console.log(chalk.cyan(msg))
// 前面一堆依赖引入和函数定义等
async function main(){// 版本校验// run tests before releasestep('\nRunning tests...')// update all package versions and inter-dependenciesstep('\nUpdating cross dependencies...')// build all packages with typesstep('\nBuilding all packages...')// generate changelogstep('\nCommitting changes...')// publish packagesstep('\nPublishing packages...')// push to GitHubstep('\nPushing to GitHub...')
}main().catch(err => {console.error(err)
})

上面的main函数省略了很多具体函数实现。接下来我们拆解 main 函数。

4.2 确认要发布的版本

第一段代码虽然比较长,但是还好理解。主要就是确认要发布的版本。

调试时,我们看下这段的两张截图,就好理解啦。

终端输出选择版本号
终端输入确认版本号
// 根据上文 mini 这句代码意思是 yarn run release 3.2.4 
// 取到参数 3.2.4
let targetVersion = args._[0]if (!targetVersion) {// no explicit version, offer suggestionsconst { release } = await prompt({type: 'select',name: 'release',message: 'Select release type',choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])})// 选自定义if (release === 'custom') {targetVersion = (await prompt({type: 'input',name: 'version',message: 'Input custom version',initial: currentVersion})).version} else {// 取到括号里的版本号targetVersion = release.match(/\((.*)\)/)[1]}
}// 校验 版本是否符合 规范
if (!semver.valid(targetVersion)) {throw new Error(`invalid target version: ${targetVersion}`)
}// 确认要 release
const { yes } = await prompt({type: 'confirm',name: 'yes',message: `Releasing v${targetVersion}. Confirm?`
})// false 直接返回
if (!yes) {return
}

4.3 执行测试用例

// run tests before release
step('\nRunning tests...')
if (!skipTests && !isDryRun) {await run(bin('jest'), ['--clearCache'])await run('yarn', ['test', '--bail'])
} else {console.log(`(skipped)`)
}

4.4 更新所有包的版本号和内部 vue 相关依赖版本号

这一部分,就是更新根目录下package.json 的版本号和所有 packages 的版本号。

// update all package versions and inter-dependencies
step('\nUpdating cross dependencies...')
updateVersions(targetVersion)
function updateVersions(version) {// 1. update root package.jsonupdatePackage(path.resolve(__dirname, '..'), version)// 2. update all packagespackages.forEach(p => updatePackage(getPkgRoot(p), version))
}

4.4.1 updatePackage 更新包的版本号

function updatePackage(pkgRoot, version) {const pkgPath = path.resolve(pkgRoot, 'package.json')const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))pkg.version = versionupdateDeps(pkg, 'dependencies', version)updateDeps(pkg, 'peerDependencies', version)fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
}

主要就是三种修改。

1. 自己本身 package.json 的版本号
2. packages.json 中 dependencies 中 vue 相关的依赖修改
3. packages.json 中 peerDependencies 中 vue 相关的依赖修改

一图胜千言。我们执行yarn release --drygit diff 查看的 git 修改,部分截图如下。

更新的版本号举例

4.4.2 updateDeps 更新内部 vue 相关依赖的版本号

function updateDeps(pkg, depType, version) {const deps = pkg[depType]if (!deps) returnObject.keys(deps).forEach(dep => {if (dep === 'vue' ||(dep.startsWith('@vue') && packages.includes(dep.replace(/^@vue\//, '')))) {console.log(chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`))deps[dep] = version}})
}

一图胜千言。我们在终端执行yarn release --dry。会看到这样是输出。

更新 Vue 相关依赖的终端输出

也就是这句代码输出的。

console.log(chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`)
)

4.5 打包编译所有包

// build all packages with types
step('\nBuilding all packages...')
if (!skipBuild && !isDryRun) {await run('yarn', ['build', '--release'])// test generated dts filesstep('\nVerifying type declarations...')await run('yarn', ['test-dts-only'])
} else {console.log(`(skipped)`)
}

4.6 生成 changelog

// generate changelog
await run(`yarn`, ['changelog'])

yarn changelog 对应的脚本是conventional-changelog -p angular -i CHANGELOG.md -s

4.7 提交代码

经过更新版本号后,有文件改动,于是git diff。是否有文件改动,如果有提交。

git add -Agit commit -m 'release: v${targetVersion}'

const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })
if (stdout) {step('\nCommitting changes...')await runIfNotDry('git', ['add', '-A'])await runIfNotDry('git', ['commit', '-m', `release: v${targetVersion}`])
} else {console.log('No changes to commit.')
}

4.8 发布包

// publish packages
step('\nPublishing packages...')
for (const pkg of packages) {await publishPackage(pkg, targetVersion, runIfNotDry)
}

这段函数比较长,可以不用细看,简单说就是 yarn publish 发布包。我们 yarn release --dry后,这块函数在终端输出的如下:

发布终端输出命令

值得一提的是,如果是 vue 默认有个 tagnext。当 Vue 3.x 是默认时删除。

} else if (pkgName === 'vue') {// TODO remove when 3.x becomes defaultreleaseTag = 'next'
}

也就是为什么我们现在安装 vue3 还是 npm i vue@next命令。

async function publishPackage(pkgName, version, runIfNotDry) {// 如果在 跳过包里 则跳过if (skippedPackages.includes(pkgName)) {return}const pkgRoot = getPkgRoot(pkgName)const pkgPath = path.resolve(pkgRoot, 'package.json')const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))if (pkg.private) {return}// For now, all 3.x packages except "vue" can be published as// `latest`, whereas "vue" will be published under the "next" tag.let releaseTag = nullif (args.tag) {releaseTag = args.tag} else if (version.includes('alpha')) {releaseTag = 'alpha'} else if (version.includes('beta')) {releaseTag = 'beta'} else if (version.includes('rc')) {releaseTag = 'rc'} else if (pkgName === 'vue') {// TODO remove when 3.x becomes defaultreleaseTag = 'next'}// TODO use inferred release channel after official 3.0 release// const releaseTag = semver.prerelease(version)[0] || nullstep(`Publishing ${pkgName}...`)try {await runIfNotDry('yarn',['publish','--new-version',version,...(releaseTag ? ['--tag', releaseTag] : []),'--access','public'],{cwd: pkgRoot,stdio: 'pipe'})console.log(chalk.green(`Successfully published ${pkgName}@${version}`))} catch (e) {if (e.stderr.match(/previously published/)) {console.log(chalk.red(`Skipping already published: ${pkgName}`))} else {throw e}}
}

4.9 推送到 github

// push to GitHub
step('\nPushing to GitHub...')
// 打 tag
await runIfNotDry('git', ['tag', `v${targetVersion}`])
// 推送 tag
await runIfNotDry('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
// git push 所有改动到 远程  - github
await runIfNotDry('git', ['push'])
// yarn run release --dry// 如果传了这个参数则输出 可以用 git diff 看看更改// const isDryRun = args.dry
if (isDryRun) {console.log(`\nDry run finished - run git diff to see package changes.`)
}// 如果 跳过的包,则输出以下这些包没有发布。不过代码 `skippedPackages` 里是没有包。
// 所以这段代码也不会执行。
// 我们习惯写 arr.length !== 0 其实 0 就是 false 。可以不写。
if (skippedPackages.length) {console.log(chalk.yellow(`The following packages are skipped and NOT published:\n- ${skippedPackages.join('\n- ')}`))
}
console.log()

我们 yarn release --dry后,这块函数在终端输出的如下:

发布到github

到这里我们就拆解分析完 main 函数了。

整个流程很清晰。

1. 确认要发布的版本
2. 执行测试用例
3. 更新所有包的版本号和内部 vue 相关依赖版本号3.1 updatePackage 更新包的版本号3.2 updateDeps 更新内部 vue 相关依赖的版本号
4. 打包编译所有包
5. 生成 changelog
6. 提交代码
7. 发布包
8. 推送到 github

用一张图总结则是:

vue 发布流程

看完vue-next/scripts/release.js,感兴趣还可以看vue-next/scripts文件夹下其他代码,相对行数不多,但收益较大。

5. 总结

通过本文学习,我们学会了这些。

1. 熟悉 vuejs 发布流程
2. 学会调试 nodejs 代码
3. 动手优化公司项目发布流程

同时建议自己动手用 VSCode 多调试,在终端多执行几次,多理解消化。

vuejs发布的文件很多代码我们可以直接复制粘贴修改,优化我们自己发布的流程。比如写小程序,相对可能发布频繁,完全可以使用这套代码,配合miniprogram-ci[14],再加上一些自定义,加以优化。

当然也可以用开源的 release-it[15]

同时,我们可以:

引入 git flow[16],管理git分支。估计很多人不知道windows git bash已经默认支持 git flow命令。

引入 husky[17] 和 lint-staged[18] 提交commit时用ESLint等校验代码提交是否能够通过检测。

引入 单元测试 jest[19],测试关键的工具函数等。

引入 conventional-changelog[20]

引入 git-cz[21] 交互式git commit

等等规范自己项目的流程。如果一个候选人,通过看vuejs发布的源码,积极主动优化自己项目。我觉得面试官会认为这个候选人比较加分。

看开源项目源码的好处在于:一方面可以拓展视野,另外一方面可以为自己所用,收益相对较高。

参考资料

[1]

vue-next: https://github.com/vuejs/vue-next

[2]

更多可点击 阅读原文 查看

最近组建了一个江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。


推荐阅读

我在阿里招前端,该怎么帮你(可进面试群)
我读源码的经历

初学者也能看懂的 Vue3 源码中那些实用的基础工具函数
老姚浅谈:怎么学JavaScript?

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》多篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,活跃在知乎@若川,掘金@若川。致力于分享前端开发经验,愿景:帮助5年内前端人走向前列。

识别方二维码加我微信、拉你进源码共读

今日话题

略。欢迎分享、收藏、点赞、在看我的公众号文章~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wireshark使用教程 linux,Linux入门教程:ubuntu下安装wireshark(以及配置非root),这个强大的工具可以捕...

Linux入门教程:ubuntu下安装wireshark(以及配置非root),这个强大的工具可以捕Wireshark是世界上最流行的网络分析工具。这个强大的工具可以捕捉网络中的数据,并为用户提供关于网络和上层协议的各种信息。与很多其他网络工具一样,Wireshark也使用pcap net…

IronPython和C#执行速度对比

其实我自己对执行速度这个问题本来并没有什么兴趣,因为以前的经验告诉我:除非是运算密集型的程序,否则脚本语言和编译型语言使用起来速度没有多大差别。但是我们公司有个人知道我的想法以后,天天在我耳边嚷嚷脚本运行速度太慢&…

基于超级账本Fabric的供应链跟踪解决方案【开源】

2019独角兽企业重金招聘Python工程师标准>>> 本项目为基于Hyperledger Fabric区块链的供应链资产跟踪解决方案,项目主要包括链码和Web应用两部分。Fabric链码采用GOLANG开发,负责维护资产的状态,后台为采用Node.js开发的Web应用&a…

同理心案例及故事分享_神经形态,视觉可及性和同理心

同理心案例及故事分享“A good UX designer has empathy”.“优秀的UX设计人员具有同理心”。 This is something every UX designer has heard at some point in their career. Empathy helps us get into the mindset of the user and build solutions that solve real probl…

纯CSS实现beautiful按钮

大家好,我是若川。邀你进源码共读群学习交流。今天分享一篇好文。可收藏~近期工作中遇到一个需求——实现一些酷炫的按钮,看到效果图之后,按钮确实漂亮,有弹跳、颜色渐变、扫光、霓虹灯,瞬间激起了我的好奇…

linux的内核有多小,Linux 内核有小bug?

今天读着读着Linux代码,竟然无意中发现Linux 0.11内核有个小bug,呵呵,人非圣贤孰能无过。// 在目录项数据块中搜索匹配指定文件名的目录项,首先让de 指向数据块,并在不超过目录中目录项数// 的条件下,循环执…

菜单窗口_菜单

菜单窗口The Hamburger Menu widget is on every other site nowadays. It has become synonymous with the web and, perhaps even more so, with web development. Have, for instance, a look at Dribbble or Codepen. There you’ll find a fair share of examples. They c…

怎么在PDF上修改文字,PDF修改文字的步骤

怎么在PDF文件上修改文字呢?其实现在的很多的PDF文件上会出现文字错误的情况,想要修改PDF文件上面的文字却不知道怎么修改,想要修改PDF文件还是比较简单的,使用专业的PDF编辑器就可以进行操作了,下面小编就为大家分享一…

读完 Vue 发布源码,小姐姐回答了 leader 的提问,并优化了项目发布流程~

大家好,我是若川。这是 源码共读 第三期活动,纪年小姐姐的第三次投稿。纪年小姐姐学习完优化了自己的项目发布流程,而且回答了leader对她的提问,来看看她的思考和实践。第三期是 Vue 3.2 发布了,那尤雨溪是怎么发布 Vu…

小程序 富文本自适应屏幕_自适应文本:跨屏幕尺寸构建可读文本

小程序 富文本自适应屏幕Many of you may already know about responsive web design. Cited from Wikipedia, responsive web design (RWD) is an approach to web design that makes web pages render well on a variety of devices and windows or screen sizes. The respon…

Vue、React 之间如何实现代码移植?

大家好,我是若川。面对前端最火的两个框架,学 React 还是 Vue ?这可能是每个前端人都曾纠结过的问题。不过,现在你不用纠结了——因为很多公司都是两个框架都有大量的应用,取决于不同团队的技术选型,特别是…

linux mariadb 乱码,配置mariadb远程访问权限,解决数据库乱码问题

配置mariadb远程访问权限:1)登录数据库:# mysql -uroot -p2)配置授权数据库用户远程访问权限,%表示所有远程IP,也可以指定IP。WITH GRANT OPTION表示mysql数据库的grant表中重新加载权限数据:GRANT ALL PRIVILEGES ON *.* TO 用户…

平面设计师和ui设计师_游戏设计师的平面设计

平面设计师和ui设计师Design is a very ancient practice, but graphic design really found its core principles post World War One. Games are also very ancient but video games are still finding their feet. I think graphic design has a few things to teach people…

java合成海报的工具类

2019独角兽企业重金招聘Python工程师标准>>> package io.renren.common.utils;import cn.hutool.core.lang.Console; import io.renren.modules.oss.cloud.OSSFactory;import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import java.a…

a说b说谎b说c说谎说d说_说谎的眼睛及其同伙

a说b说谎b说c说谎说d说The eye is a complex and temperamental organ. By the end of this article, designers will have a better understanding of how the eye works with the brain, how it deconstructs images that the brain stitches back up again, and how the two…

一名运营,自学一年前端,成功入职杭州某独角兽企业,他的面试经验和学习方法等分享...

大家好,我是若川。这是我的微信群里小伙伴年年 的投稿。他是19年毕业,之前做的是运营相关的工作,在我的交流群里非常活跃,自学一年前端,目前成功转行入职杭州一家独角兽企业。相信他的文章能带给大家一些启发和激励。0…

百度指数可视化_可视化指数

百度指数可视化Abstract:– Analysis of the visual representations of exponentials.– Proposals to solve current visualization issues.– Call to discussion to come up with a better visual representation convention.抽象: –分析指数的视觉表示形式。…

阿里云谦大佬:时间精力有限的情况下如何高效学习前端?

大家好,我是若川。最近组织了源码共读活动1个月,200人,一起读了4周源码,欢迎加我微信 ruochuan12 进群参与。今天分享一篇阿里云谦大佬的文章。昨天在群里也有小伙伴说到:大佬们是需要什么学什么,新手一般是…

sketch钢笔工具_Sketch和Figma,不同的工具等于不同的结果

sketch钢笔工具We like to compare the difference between various design programs and debate about which one is the most powerful. But we often forget to reflect on how using one of these tools is impacting our product. A powerful artist would say that he ca…

程序下载

Zaxis终端前置机 版 本下 载特 性1.20.1104.102ZaxisSetup.rar 分类: 程序下载转载于:https://www.cnblogs.com/baijinlong/archive/2011/05/13/2045263.html