
暂无个人介绍
想要快速构建实际应用,离不开一个好的应用模版,React作为大厂出品工具,有着稳定性和可维护性的保障,同时可以使用相关的全套全家桶(React + React-router + Axios + Mobx + Antd)进行连贯敏捷开发,本文将从如何在云开发平台创建项目应用模版,基于应用模版创建《后台管理》项目,以及上传并且通过云平台将项目上线部署应用,为项目开发提供更加便捷的操作环境。一 、通过云开发平台快速创建初始化应用1.创建相关应用模版请参考链接:https://developer.aliyun.com/article/878171?spm=a2c6h.12873581.0.dArticle878171.c61253e8nVBtAv2.完成创建后就可以在github中查看到新增的react仓库二 、本地编写《后台管理》项目1.将应用模版克隆到本地首先假定你已经安装了Git、node,没有安装请移步node官网进行安装。克隆项目:git clone + 项目地址进入项目文件cd create-react-app切换到feature/1.0.0 分支上 git checkout feature/1.0.0使用一下命令全局安装 React :npm install -g create-react-app安装依赖包npm install启动服务npm start这里打开浏览器3000端口,并出现默认页面。2.架构与效果预览《后台管理》项目架构效果预览3.初始化项目初始化package.jsonnpm init安装webpacknpm add -D webpack webpack-cli webpack-merge项目中使用的Webpack版本是^5.10.0,Webpack4.0 打包构建做了很多默认的优化配置,不少配置项无需配置或更改。比如:针对开发模式的加快打包速度,合并chunk; 针对生产模式的代码压缩,减少打包体积等。// 一部分默认配置 optimization: { removeAvailableModules: true, // 删除已解决的chunk (默认 true) removeEmptyChunks: true, // 删除空的chunks (默认 true) mergeDuplicateChunks: true // 合并重复的chunk (默认 true) } // 针对生产环境默认配置 optimization: { sideEffects:true, //配合tree shaking splitChunks: {...}, //拆包 namedModules: false, // namedChunks:false 不启用chunk命名,默认自增id minimize: true, // 代码压缩 }根据开发环境/生产环境 区分webpack配置非常有必要,可以加快开发环境的打包速度,有时候遇到开发环境打包过慢,可以排查下是否配置有误(比如开发环境开启了代码压缩等)。项目中配合webpack-merge根据开发环境/生产环境进行拆分配置:Webpack4.0发布已经很长时间了,相信基本上项目都已迁移至4.0,在这里就不多赘述了。配置Html模版安装:npm add -D html-webpack-plugin配置:const srcDir = path.join(__dirname, "../src"); plugins: [ new HtmlWebpackPlugin({ template: `${srcDir}/index.html` }) ]配置本地服务及热更新安装:npm add -D webpack-dev-server clean-webpack-plugin开发环境利用webpack-dev-server搭建本地 web server,并启用模块热更新(HMR)。为方便开发调试,转发代理请求(本例中配合axios封装 转发接口到easy-mock在线平台)配置:mode: "development", // 开发模式 devServer: { // 本地服务配置 port: 9000, hot: true, open: false, historyApiFallback: true, compress: true, proxy: { // 代理 "/testapi": { target: "https://www.easy-mock.com/mock/5dff0acd5b188e66c6e07329/react-template", changeOrigin: true, secure: false, pathRewrite: { "^/testapi": "" } } } }, plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ],配置Babel安装:npm add -D babel-loader @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react babel-plugin-import @babel/plugin-proposal-class-properties @babel/plugin-proposal-decoratorsWebpack中Babel配置,是比较重要的一环。关系着ES6语法、React jsx、Mobx等语法经过打包后能否正常运行。其中:@babel/preset-react转换React jsx语法;@babel/plugin-proposal-class-properties 转换 Class语法;@babel/plugin-proposal-decorators 转换 Mobx 等更高级的语法;babel-plugin-import 配合实现React组件的按需加载;这里需要注意Babel7.0 相较于Babel6.0的区别。配置:module: { rules: [ { test: /\.(js|jsx)$/, include: [srcDir], use: ["babel-loader?cacheDirectory=true"] }, ] }.babelrc 文件配置{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/transform-runtime", [ "@babel/plugin-proposal-decorators", { "legacy": true } ], ["@babel/plugin-proposal-class-properties", { "loose": true }], [ "import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" // `style: true` 会加载 less 文件 } ] ] }处理Less样式和图片等资源安装:npm add -D less less-loader style-loader css-loader url-loader mini-css-extract-plugin postcss-loader autoprefixer其中:less-loader、style-loader、css-loader处理加载less、css文件;postcss-loader、autoprefixer处理css样式浏览器前缀兼容;url-loader处理图片、字体文件等资源;mini-css-extract-plugin 分离css成单独的文件;配置:const MiniCssExtractPlugin = require("mini-css-extract-plugin"); ... module: { rules: [ { test: /\.less$/, use: [ devMode ? "style-loader" : MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader" ] }, { test: /\.css$/, use: [ devMode ? "style-loader" : MiniCssExtractPlugin.loader, "css-loader", "postcss-loader" ] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use: ["url-loader"], include: [srcDir] }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: ["url-loader"], include: [srcDir] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, use: ["url-loader"], include: [srcDir] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].[contenthash:8].css", chunkFilename: "chunk/[id].[contenthash:8].css" }), ],配置postcss .postcssrc.js 文件// .postcssrc.js module.exports = { plugins: { autoprefixer: {} } }; // package.json中配置兼容浏览器 "browserslist": [ "> 1%", "last 2 versions", "not ie <= 10" ]利用happypack多线程打包安装:npm add -D happypack配置:const os = require("os"); const HappyPack = require("happypack"); const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); module: { rules: [ { test: /\.(js|jsx)$/, include: [srcDir], exclude: /(node_modules|bower_components)/, use: ["happypack/loader?id=happybabel"] }, ] }, plugins: [ //开启 happypack 的线程池 new HappyPack({ id: "happybabel", loaders: ["babel-loader?cacheDirectory=true"], threadPool: happyThreadPool, cache: true, verbose: true }), ]生产环境 拆分模块根据实际项目情况拆分模块,配合异步加载,防止单个文件过大。optimization: { runtimeChunk: { name: "manifest" }, splitChunks: { chunks: "all", //默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效 cacheGroups: { dll: { test: /[\\/]node_modules[\\/](react|react-dom|react-dom-router|babel-polyfill|mobx|mobx-react|mobx-react-dom|antd|@ant-design)/, minChunks: 1, priority: 2, name: "dll" }, codeMirror: { test: /[\\/]node_modules[\\/](react-codemirror|codemirror)/, minChunks: 1, priority: 2, name: "codemirror" }, vendors: { test: /[\\/]node_modules[\\/]/, minChunks: 1, priority: 1, name: "vendors" } } } }其他配置引入 ESLint 与 Prettier 配合,规范化团队项目代码开发,统一代码风格。npm add -D prettier babel-eslint eslint eslint-loader eslint-config-airbnb eslint-config-prettier eslint-plugin-babel eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react具体配置详见 **/build目录** 下 https://github.com/now1then/react-web-pro/tree/master/buildnpm scriptspackage.json 文件{ ... "scripts": { "start": "webpack-dev-server --color --inline --progress --config build/webpack.dev.js", // "build": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js", "build:report": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js", "build:watch": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js" }, ... }命令行运行:// 命令行执行// 运行开发环境;npm start// 生产环境打包压缩;npm build// 图形化分析打包文件大小;npm build:report// 方便排查生产环境打包后文件的错误信息(文件source map);npm build:watch其中 build:report 、build:watch 能够实现功能,是在build/webpack.prod.js 中有如下代码:// 方便排查生产环境打包后文件的错误信息(文件source map) if (process.env.npm_lifecycle_event == "build:watch") { config = merge(config, { devtool: "cheap-source-map" }); } // 图形化分析打包文件大小 if (process.env.npm_lifecycle_event === "build:report") { const BundleAnalyzerPlugin = require("webpack-bundle-analyzer") .BundleAnalyzerPlugin; config.plugins.push(new BundleAnalyzerPlugin()); }项目代码架构npm add react react-dom react-router-dom mobx mobx-react mobx-react-router axios antd moment4.函数化Hooks当前React版本已更新到16.12,Hooks 完全应该成为 React 使用的主流。本项目中将完全拥抱Hook,一般不再用 class 来实现组件。以下为部分实现代码(可暂忽略mobx的使用):import React, { useState, useEffect, useContext } from 'react'; import { observer } from 'mobx-react'; import { Button } from 'antd'; import Store from './store'; import './style.less'; const HomePage = () => { // useContext 订阅mobx数据 const pageStore = useContext(Store); // useState state状态 const [num, setNum] = useState(0); // useEffect副作用 useEffect(() => { pageStore.qryTableDate(); }, []); return ( <div className="page-home page-content"> <h2>{pageStore.pageTitle}</h2> <div> <span>num值:{num}</span> <Button type="primary" size="small" style={{ marginLeft: 10 }} onClick={() => setNum(num + 1)} >+1</Button> </div> </div> ); }; export default observer(HomePage);5.Router路由配置项目是单页应用,路由配置一般分为约定式动态路由和集中配置式路由。在 React 的世界里,直接采用成熟的react-router工具管理页面路由。我们现在说到react-router,基本上都是在说 react-router 的第4版之后的版本,当前的最新版本已经更新到5.1.x了。当前react-router支持动态路由,完全用React组件来实现路由,在渲染过程中动态设置路由规则,匹配命中规则加载对应页面组件。本项目采用集中配置式路由(方便路由鉴权、从服务端接口获取菜单路由配置等),同时兼顾方便地设置侧边菜单栏。 当然为简单起见,项目中读取本地静态菜单配置,也暂未引入路由鉴权。6.静态路由配置 src/routes/config.js :import React, { lazy } from "react"; import BasicLayout from "@/layouts/BasicLayout"; import BlankLayout from "@/layouts/BlankLayout"; const config = [ { path: "/", component: BlankLayout, // 空白页布局 childRoutes: [ // 子菜单路由 { path: "/login", // 路由路径 name: "登录页", // 菜单名称 (不设置,则不展示在菜单栏中) icon: "setting", // 菜单图标 component: lazy(() => import("@/pages/Login")) // 懒加载 路由组件 }, // login等没有菜单导航栏等基本布局的页面, 要放在基本布局BasicLayout之前。 { path: "/", component: BasicLayout, // 基本布局框架 childRoutes: [ { path: "/welcome", name: "欢迎页", icon: "smile", component: lazy(() => import("@/pages/Welcome")) }, {... /* 其他 */}, { path: "/", exact: true, redirect: "/welcome" }, { path: "*", exact: true, redirect: "/exception/404" } ] } ] } ]; export default config;上面是静态路由的一部分配置,注意:<Router>中会用<Switch>包裹,会匹配命中的第一个。"/login"等没有菜单导航栏等基本布局的页面, 要放在基本布局BasicLayout之前。利用<Suspense>和React.lazy()实现页面组件懒加载。7.路由组建渲染 src/routes/AppRouter.js :import React, { lazy, Suspense } from "react"; import LoadingPage from "@/components/LoadingPage"; import { HashRouter as Router, Route, Switch, Redirect } from "react-router-dom"; import config from "./config"; const renderRoutes = routes => { if (!Array.isArray(routes)) { return null; } return ( <Switch> {routes.map((route, index) => { if (route.redirect) { return ( <Redirect key={route.path || index} exact={route.exact} strict={route.strict} from={route.path} to={route.redirect} /> ); } return ( <Route key={route.path || index} path={route.path} exact={route.exact} strict={route.strict} render={() => { const renderChildRoutes = renderRoutes(route.childRoutes); if (route.component) { return ( <Suspense fallback={<LoadingPage />}> <route.component route={route}> {renderChildRoutes} </route.component> </Suspense> ); } return renderChildRoutes; }} /> ); })} </Switch> ); }; const AppRouter = () => { return <Router>{renderRoutes(config)}</Router>; }; export default AppRouter;8.路由 hooks 语法react-router-dom 也已经支持 hooks语法,获取路由信息或路由跳转,可以使用新的hooks 函数:[useHistory](https://reacttraining.com/react-router/core/api/Hooks/usehistory):获取历史路由,回退、跳转等操作;useLocation:查看当前路由信息;[useParams](https://reacttraining.com/react-router/core/api/Hooks/useparams):读取路由附带的params参数信息;[useRouteMatch](https://reacttraining.com/react-router/core/api/Hooks/useroutematch):匹配当前路由;只要包裹在中的子组件都可以通过这几个钩子函数获取路由信息。代码演示:import { useHistory } from "react-router-dom"; function HomeButton() { const history = useHistory(); function onClick() { history.push("/home"); } return ( <button type="button" onClick={onClick}> 跳转Home页 </button> ); }9.结合mobx管理数据状态项目中是否使用状态管理工具或使用何种管理工具,依据实际项目情况而定。本项目使用自己比较熟悉的Mobx,Mobx是一个功能强大,上手非常容易的状态管理工具。为了使用简洁及管理方便,在组织上,分为全局公共数据状态和页面数据状态。公用数据状态存放在/src/stores目录下;页面几数据存放于对应页面目录下。在实现上,利用mobx + useContext Hook特性 实现函数式组件的状态管理。具体在于利用React的createdContext构建包含Mobx 的context上下文;函数式组件中使用useContext Hook 订阅Mobx数据变化。页面级store.js 代码:import { createContext } from "react"; import { observable, action, computed } from "mobx"; import request from "@/services/newRequest"; class HomeStore { @observable tableData = []; @observable pageTitle = "Home主页"; @observable loading = false; @action.bound setData(data = {}) { Object.entries(data).forEach(item => { this[item[0]] = item[1]; }); } // 列表数据 @action.bound async qryTableDate(page = 1, size = 10) { this.loading = true; const res = await request({ url: "/list", method: "post", data: { page, size } }); if (res.success) { const resData = res.data || {}; console.log(resData); } this.loading = false; } } export default createContext(new HomeStore());页面组件代码 :import React, { useContext } from "react"; import { observer } from "mobx-react"; import Store from "./store"; import "./style.less"; const HomePage = () => { const pageStore = useContext(Store); return ( <div className="page-home page-content"> home页面 <h2>{pageStore.pageTitle}</h2> </div> ); }; export default observer(HomePage);以上为部分演示代码,具体业务实现可以查看项目代码。10.Axios Http 请求封装Axios请求封装,具体代码见 /src/services/newRequest.js11.UI组件及页面布局UI组件使用优秀的Ant Design 组件库,注意使用 babel-plugin-import 配置实现组件的按需加载。本项目的内部页面布局采用 Antd 上经典的布局方式:页面布局需要合理拆分模块,左侧菜单导航栏根据静态菜单渲染。实际完整代码详见项目,以下为BasicLayout组件:import React from "react"; import { Layout } from "antd"; import SiderMenu from "../SiderMenu"; import MainHeader from "../MainHeader"; import MainFooter from "../MainFooter"; import "./style.less"; const BasicLayout = ({ route, children }) => { return ( <Layout className="main-layout"> {/* 左侧菜单导航 */} <SiderMenu routes={route.childRoutes} /> <Layout className="main-layout-right"> {/* 顶部展示布局 */} <MainHeader></MainHeader> <Layout.Content className="main-layout-content"> {/* 实际页面布局 */} {children} {/* <MainFooter></MainFooter> */} </Layout.Content> </Layout> </Layout> ); }; export default BasicLayout;对于登录页等页面无需套在上面的基本布局之类,需要单独处理(菜单配置在BasicLayout配置之前)。三 、云端一键部署上线应用1.上传代码git add . git commit -m '添加你的注释' git push2.在日常环境部署一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。3.配置自定义域名在线上环境上线配置线上环境自定义域名。在功能开发验证完成后要在线上环境进行部署,在线上环境的「部署配置」-「自定义域名」中填写自己的域名。例如我们添加一个二级域名 company.workbench.fun 来绑定我们部署的前端应用。然后复制自定义域名下方的API网关地址对添加的二级域名进行CNAME配置。配置CNAME地址。复制好 API网关域名地址后,来到你自己的域名管理平台(此示例中的域名管理是阿里云的域名管理控制台,请去自己的域名控制台操作)。添加记录的「记录类型」选择「CNAME」,在「主机记录」中输入你要创建的二级域名,这里我们输入「company」,在「记录值」中粘贴我们之前复制的 API网关域名地址,「TTL」保留默认值或者设置一个你认为合适的值即可。在线上环境部署上线。回到云开发平台的应用详情页面,按照部署的操作,点击线上环境的「部署按钮」,部署完成以后就在你自定义的域名进行了上线。CNAME 生效之后,我们输入 company.workbench.fun(示例网址) 可以打开部署的页面。至此,如何部署一个应用到线上环境,如何绑定自己的域名来访问一个线上的应用就完成了,赶紧部署自己的应用到线上环境,用自己的域名玩起来吧 ;)一键创建React应用模版链接 :https://workbench.aliyun.com/application/front/create?fromConfig=12&fromRepo=sol_github_12参考文献:https://juejin.cn/post/6844904035099623437【特别活动】阿里云云开发平台发布两年啦,感谢你对云开发长期的支持!快来分享你在云开发当中的开发感受,心得体会,以及成长经验吧!(包含但不仅限于文章、个人作品、毕业设计,企业应用等)。 所有在云开发平台做过相关应用开发的同学,分享并提交我与云开发的故事均可以获得优酷VIP月卡一张,我们会选择50个用户的故事和留言发放云开发优秀用户奖,前三名用户还将额外获得AirPods无线耳机一副,同时也会在相应的社区、媒体上对用户故事进行宣传。https://survey.aliyun.com/apps/zhiliao/EGhLta4n8?spm=a2c6h.activitiesspring.J_2503981410.4.2bc369feAz0051
你好,春天蜕去厚厚的冬装,远离冬日的困乏与疲倦,是时候在春暖花开之际,跟随春天的步伐,为云开发能力增长设立新的Flag。为了助力开发者增长开发能力,实践开发项目,在3月末尾之际,云开发平台推出“12天云开发打卡挑战赛”,跟随阿里云开发专家,你将从9大课程了解如何搭建云原生应用!活动入口:https://workbench.aliyun.com/activities/spring2学习云开发,收获云奖励想要跟随企业数字化春风步伐,作为一名开发者需要不断学习新知,本次12天云开发打卡活动,包含9大视频课程和3类实践项目。云开发静态应用入门部分将由阿里云智能技术专家亲自授课,从云开发前端应用托管,线上一键部署,再到真实案例的讲解,阿里专家将通过多年的开发经验,为广大学习者讲述云开发当中的“门道”。在后端配置进阶部分,阿里云智能技术专家将分别讲述后端数据库的连接,应用上线部署绑定域名,以及后端部署应用的方法,从技术层面,更细致,更深入地为后端开发者提供应用上线技能,助力后端开发者们早日踏入“云领域”,实现开发效率成倍增长!学习了静态应用知识和后端配置方法后,本次活动还提供了更加丰富的开发场景,面向钉钉和支付宝开发者,阿里云智能技术专家将带领你实战演练钉钉企业从组织的创立,账户绑定,到应用的搭建,开发、调试、上线,运维的完整过程,以及讲解支付宝开发者中心如何进行前后端联动调试,为开发者们提供更丰富的案例技能!“实践检验真知”,本次活动还将为云开发者提供3类试炼项目,通过天猫精灵语音高分电影项目,开发者可以亲自实践天猫应用后台开发环境,以及代码调试等,同时利用卡通头像和Web ToDo清单项目,开发者可以了解网站的上线,绑定域名等云开发知识。完成了9大视频课,和3类试炼项目,就可以收获到《小米电动牙刷》一副!还在等待什么,马上学习起来吧!活动和奖品说明本次活动将于2022年3月31日开始,到2022年4月13日截止,活动期间完成所有任务,即可获得领取《小米电动牙刷》的资格,领取时间为4月13日10:00:00-23:59:59,奖品数量有限,先到先得。任务部分有完成时效限制,《云开发静态应用入门》所有任务完成时间为3月31日-4月7日,《云开发后端配置进阶》所有任务完成时间为4月7日-4月10日,《云开发端侧应用实战》所有任务完成时间为4月10日-4月13日,超过各部分的完成时间后学习,均不能正常完成任务打卡,活动期间遇到任何问题,可添加钉钉群:34096194,群内有服务人员解决开发问题,大牛切磋“云开发技法”,同时群内更有各类“云开发活动”等待着你的参与!【特别活动】阿里云云开发平台发布两年啦,感谢你对云开发长期的支持!快来分享你在云开发当中的开发感受,心得体会,以及成长经验吧!(包含但不仅限于文章、个人作品、毕业设计,企业应用等)。 所有在云开发平台做过相关应用开发的同学,分享并提交我与云开发的故事均可以获得优酷VIP月卡一张,我们会选择50个用户的故事和留言发放云开发优秀用户奖,前三名用户还将额外获得AirPods无线耳机一副,同时也会在相应的社区、媒体上对用户故事进行宣传。https://survey.aliyun.com/apps/zhiliao/EGhLta4n8?spm=a2c6h.activitiesspring.J_2503981410.4.2bc369feAz0051
春天的脚步愈发临近,相信很多小伙伴已经开始规划自己的踏春计划了,无论是欣赏名胜古迹,还是走访风土人文,你都需要提前准备一份旅游清单!有了这款Angular旅游计划应用,从地点到预算,它都能帮助你创建自己的踏春足迹!踏春正当时,马上跟随本文,从云平台创建应用模版,本地编写《旅游清单》项目,到一键部署上线开始,一步一步创建自己的踏春计划,让一场说走就走的旅行,从当下发生吧!一 、通过云开发平台快速创建初始化应用1.创建相关应用模版请参考链接: https://developer.aliyun.com/article/874644?spm=a2c6h.12873581.0.dArticle874644.47cf53e8jQjrWo2.完成创建后就可以在github中查看到新增的Angular 仓库二 、 本地编写《旅游清单》项目1.将应用模版克隆到本地首先假定你已经安装了Git、node,没有安装请移步node官网进行安装。克隆项目:git clone + 项目地址进入项目文件cd Angular切换到feature/1.0.0 分支上 git checkout feature/1.0.0使用一下命令全局安装 Angular CLI :npm install -g @angular/cli安装依赖包npm install启动服务ng serve这里打开浏览器4200端口,并出现默认页面。2.架构与效果预览《旅游清单》项目架构其中components为组件存放区,config为公共配置区,home/newMap为页面区,mock为模拟数据区,service为应用所需服务区,如http服务,存储服务,custom.modules文件为第三方组件安置区。效果预览添加旅游规划之后:3.项目编写引入地图api<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=你的ak"></script> <script type="text/javascript" src="http://api.map.baidu.com/library/CurveLine/1.5/src/CurveLine.min.js"></script>至此,项目的基本准备工作已经做好了,下面让我们先聊一聊angular。4.angular基本语法和架构基本语法和vue类似,ng的基本语法如下:模版语法数据指令属性绑定事件绑定案例如下:<h1>{{title}}</h1> <h2 [title]="mytitle">My favorite hero is: {{ mytitle }}</h2> <p>Heroes:</p> <ul> <li *ngFor="let item of list"> {{ hero }} </li> </ul> <button (click)="onclickBtn">单机</button>以上代码可以知道,我们用{{}}插入数据,用[属性名]绑定属性,*ngFor为循环指令,类似的*ngIf为条件判断,事件绑定用(click),我们看看组件的ts文件对应的写法:import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class AppComponent { mytitle = 'Xujiang'; list = [ 'xujaing', 'zhangjiang', 'xakeng' ]; onclickBtn() { console.log('你好') } }基本架构采用angular官方提供的架构图:我们知道,一个完整的angular应该包括:模块Angular 定义了 NgModule,NgModule 为一个组件集声明了编译的上下文环境,它专注于某个应用领域、某个工作流或一组紧密相关的能力,每个 Angular 应用都有一个根模块,通常命名为 AppModule。根模块提供了用来启动应用的引导机制。 一个应用通常会包含很多功能模块。组件每个 Angular 应用都至少有一个组件,也就是根组件,它会把组件树和页面中的 DOM 连接起来。 每个组件都会定义一个类,其中包含应用的数据和逻辑,并与一个 HTML 模板相关联,该模板定义了一个供目标环境下显示的视图 比如:import { Component, OnInit } from '@angular/core'; import { LocationService } from '../../service/list'; @Component({ selector: 'app-bar', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class AppBar implements OnInit { items; constructor(private locationService: LocationService) { this.items = this.locationService.getItems(); } ngOnInit() { } }服务于依赖注入对于与特定视图无关并希望跨组件共享的数据或逻辑,可以创建服务类。 服务类的定义通常紧跟在 “@Injectable()” 装饰器之后。该装饰器提供的元数据可以让你的服务作为依赖被注入到客户组件中。例如:``` import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class Storage {} ```路由Angular 的 Router 模块提供了一个服务,它可以让你定义在应用的各个不同状态和视图层次结构之间导航时要使用的路径。如下:import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home'; import { NewMapComponent } from './newMap'; // 路由不能以‘/’开始 const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'newMap', component: NewMapComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }百度地图api及跨域问题解决我们进入百度地图官网后,去控制台创建一个应用,此时会生成对应的应用ak,如下:本地调试时将referer写成*即可,但是我们用ng的http或者fetch去请求api接口时仍会出现跨域,在网上搜集了各种资料,都没有达到效果,我们这里使用jquery的$.getScript(url),结合jsonp回调,即可解决该问题。所以先安装以下jquery:npm install jquery解决方案如下:封装http服务:import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { AK, BASE_URL } from '../config'; import * as $ from "jquery"; @Injectable({ providedIn: 'root' }) export class Http { constructor( private http: HttpClient ) { } params(data = {}) { let obj = {...data, ak: AK, output: 'json' }; let paramsStr = '?'; for(let v in obj) { paramsStr += `${v}=${obj[v]}&` }; return paramsStr.substr(0, paramsStr.length -1); } get(url, params) { return this.http.get(`${BASE_URL}${url}${this.params(params)}`) } getCors(url, params) { return new Promise((resolve, reject) => { $.getScript(`${BASE_URL}${url}${this.params(params)}`, (res, status) => { if(status === 'success') { resolve(status) } else { reject(status) } }); }) } }定义jsonp回调和接收数据变量:let locationData = null; window['cb'] = function(data) { locationData = data && data.results; } 使用: async searchLocation(v) { return await this.http.getCors('/place/v2/search', { region:v, query: v, callback: 'cb' }); }至此,应用几个主要的突破点已经解决好了,接下来我们来开发项目的核心页面和组件。按需引入materialUI组件:// custom.module.ts import { NgModule } from '@angular/core'; import { MatButtonModule, MatTooltipModule, MatBadgeModule } from '@angular/material'; @NgModule({ imports: [MatButtonModule, MatTooltipModule, MatBadgeModule], exports: [MatButtonModule, MatTooltipModule, MatBadgeModule], }) export class CustomMaterialModule { }custom.module.ts为根目录下的文件,这里我用来做存储第三方组件的位置,定义好之后在app.module.ts中引入:// material组件库 import { CustomMaterialModule } from './custom.module'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, BrowserAnimationsModule, ReactiveFormsModule, AppRoutingModule, HttpClientModule, CustomMaterialModule, ], providers: [], bootstrap: [AppComponent] })BrowserAnimationsModule主要是angular为组件提供一些动效支持的模块。 接下来我们看看入口页面:// app.component.html <div class="app-wrap"> <app-bar></app-bar> <main class="main"> <router-outlet></router-outlet> </main> <app-footer></app-footer> </div>app-bar,app-footer为我们定义好的页头页尾组件,如下:// app-bar.html <nav class="nav-bar"> <div class="logo">旅游导图+</div> <a [routerLink]="['/']">首页</a> <a [routerLink]="['/newMap']"><span [matBadge]="items.length" matBadgeOverlap="false" matBadgeColor="warn">我的大陆</span></a> </nav> // app-bar.ts import { Component, OnInit } from '@angular/core'; import { LocationService } from '../../service/list'; @Component({ selector: 'app-bar', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class AppBar implements OnInit { items; constructor(private locationService: LocationService) { this.items = this.locationService.getItems(); } ngOnInit() { } } // footer.html <footer class="footer">@开发者:{{ name }}</footer> // footer.ts import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-footer', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class AppFooter implements OnInit { name = '猪先森'; constructor() { } ngOnInit() { } }其次,页面头部组件用到了LocationService,我们来看看这个service:import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Storage } from './storage'; @Injectable({ providedIn: 'root' }) export class LocationService { items = [ { name: '北京', desc: '北京好,风景真的不错!', price: '2000', date: '2018-12-29', hasDone: true, location: { lat: 39.910924, lng: 116.413387 } }, { name: '苏州', desc: '苏州好,去了还想去,不错!', price: '2000', hasDone: true, date: '2018-12-29', location: { lat: 31.303565, lng: 120.592412 } }, { name: '上海', desc: '上海好,去了还想去,不错!', price: '2000', hasDone: true, date: '2018-12-29', location: { lat: 31.235929, lng: 121.48054 } }, { name: '武汉', desc: '武汉好,去了还想去,不错!', price: '2000', hasDone: true, date: '2018-12-29', location: { lat: 30.598467, lng: 114.311586 } } ]; constructor( private http: HttpClient, private store: Storage ) { if(store.get('list')) { this.items = store.get('list'); } } addToList(location) { this.items.push(location); this.store.set('list', this.items); } getItems() { return this.items; } clearList() { this.items = []; return this.items; } }该服务主要提供访问列表,添加旅游清单,清除清单的功能,我们利用@Injectable({ providedIn: 'root' })将服务注入根组件以便共享服务。其次我们使用自己封装的Storage服务来进行持久化数据存储,storage服务如下:import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class Storage { get(k) { return JSON.parse(localStorage.getItem(k)) } set(k, v) { localStorage.setItem(k, JSON.stringify(v)) } remove(k) { localStorage.removeItem(k) } }实现起来比较简单,这里就不多说明了。 接下来我们看看首页核心功能的实现:地图初始化路线图:代码如下:import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Input } from '@angular/core'; import { Http } from '../service/http'; import { FormBuilder } from '@angular/forms'; import { LocationService } from '../service/list'; @Component({ selector: 'app-home', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class HomeComponent implements OnInit { hasDoneList; constructor( private locationService: LocationService, private http: Http, ) { this.hasDoneList = this.locationService.getItems(); } ngOnInit() { let map = new BMap.Map("js_hover_map"); // 创建地图实例 map.centerAndZoom(new BMap.Point(118.454, 32.955), 6); map.enableScrollWheelZoom(); let hasDoneLocations = []; this.locationService.getItems().forEach(item => { item.hasDone && hasDoneLocations.push(new BMap.Point(item.location.lng,item.location.lat)) }) let curve = new BMapLib.CurveLine(hasDoneLocations, {strokeColor:"red", strokeWeight:4, strokeOpacity:0.5}); //创建弧线对象 map.addOverlay(curve); //添加到地图中 curve.enableEditing(); //开启编辑功能 } }我们在ngOninit生命周期里,初始化地图数据,根据前面我们定义的list server,把hasDone为true的数据过滤出来,显示在地图上。 接下来我们实现添加旅游清单的功能。 2. 添加旅游清单表单空间我们都用h5原生控件,我们使用angular提供的form模块,具体代码如下:import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Input } from '@angular/core'; import { Http } from '../service/http'; import { FormBuilder } from '@angular/forms'; import { LocationService } from '../service/list'; // 获取跨域数据的回调 let locationData = null; window['cb'] = function(data) { locationData = data && data.results; } @Component({ selector: 'app-home', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class HomeComponent implements OnInit { hasDoneList; checkoutForm; constructor( private formBuilder: FormBuilder, private locationService: LocationService, private http: Http, ) { this.hasDoneList = this.locationService.getItems(); this.checkoutForm = this.formBuilder.group({ name: '', price: '', date: '' }); } ngOnInit() { ... } async searchLocation(v) { return await this.http.getCors('/place/v2/search', { region:v, query: v, callback: 'cb' }); } onSubmit(customerData) { if(customerData.name) { this.searchLocation(customerData.name).then(data => { this.locationService.addToList({...customerData, location: locationData[0].location, hasDone: false}) }); } else { alert('请填写旅游地点!'); return } this.checkoutForm.reset(); } onReset() { this.checkoutForm.reset(); } } // html <div class="home-wrap"> <section class="content"> <div class="has-done"> <div class="title">我已去过:</div> <div class="visit-list"> <button *ngFor="let item of hasDoneList" class="has-btn" mat-raised-button [matTooltip]="item.desc" aria-label="按钮当聚焦或者经过时展示工具提示框"> {{ item.name }} </button> </div> </div> <div class="has-done"> <div class="title">未来规划:</div> <div class="future-list"> <form [formGroup]="checkoutForm"> <div class="form-control"> <label>地点:</label> <input type="text" formControlName="name"> </div> <div class="form-control"> <label>预算:</label> <input type="number" formControlName="price"> </div> <div class="form-control"> <label>日期:</label> <input type="date" formControlName="date"> </div> <div class="form-control"> <button mat-raised-button color="primary" class="submit-btn" type="submit" (click)="onSubmit(checkoutForm.value)">提交</button> <button mat-raised-button color="accent" class="reset-btn" (click)="onReset()">重置</button> </div> </form> </div> </div> </section> <section class="map-wrap" id="js_hover_map"></section> </div>我们使用angular提供的FormBuilder来处理表单数据,这里需要注意,我们在提交表单的时候,需要先调用百度地图的api去生成经纬度数据,之后一起添加到清单,这样做的目的是要想画路线图,我们需要给百度地图api提供经纬度数据。还有一点,由于访问涉及到跨域,我们要定义jsonp的回调,来拿到数据,如下:let locationData = null; window['cb'] = function(data) { locationData = data && data.results; }locationService的addToList方法会将数据添加到清单,并存储到storage中。 如果想了解完整代码,欢迎在我的github上查看。接下来看看我的大陆页面,其实涉及的难点不是很多,主要是根据hasDone为true或false去显示不同的样式。代码如下:// html <div class="detail"> <h1>新大陆</h1> <div class="position-list"> <div class="position-item" *ngFor="let item of list"> <span class="is-new" *ngIf="!item.hasDone">新</span> <span class="title">{{item.name}}</span> <span class="date">{{item.date}}</span> <span class="desc">{{item.desc}}</span> <span class="price">预算:{{item.price}}</span> </div> </div> </div> // ts import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Input } from '@angular/core'; import { LocationService } from '../service/list'; @Component({ selector: 'app-new-map', templateUrl: './index.html', styleUrls: ['./index.scss'] }) export class NewMapComponent implements OnInit { @Input() product; // 指定product值从父组件中传递 list; constructor( private route: ActivatedRoute, private locationService: LocationService ) { this.list = locationService.getItems(); } editItem(item) { } ngOnInit() { this.route.paramMap.subscribe(params => { // this.product = products[+params.get('productId')]; }); } }大致项目基本完成,如果想查看实际项目效果,可参考原项目作者的代码:https://github.com/MrXujiang/angularDemo三 、 云端一键部署上线应用1.上传代码git add . git commit -m '添加你的注释' git push2.在日常环境部署一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。3.配置自定义域名在线上环境上线配置线上环境自定义域名。在功能开发验证完成后要在线上环境进行部署,在线上环境的「部署配置」-「自定义域名」中填写自己的域名。例如我们添加一个二级域名 company.workbench.fun 来绑定我们部署的前端应用。然后复制自定义域名下方的API网关地址对添加的二级域名进行CNAME配置。配置CNAME地址。复制好 API网关域名地址后,来到你自己的域名管理平台(此示例中的域名管理是阿里云的域名管理控制台,请去自己的域名控制台操作)。添加记录的「记录类型」选择「CNAME」,在「主机记录」中输入你要创建的二级域名,这里我们输入「company」,在「记录值」中粘贴我们之前复制的 API网关域名地址,「TTL」保留默认值或者设置一个你认为合适的值即可。在线上环境部署上线。回到云开发平台的应用详情页面,按照部署的操作,点击线上环境的「部署按钮」,部署完成以后就在你自定义的域名进行了上线。CNAME 生效之后,我们输入 company.workbench.fun(示例网址) 可以打开部署的页面。至此,如何部署一个应用到线上环境,如何绑定自己的域名来访问一个线上的应用就完成了,赶紧部署自己的应用到线上环境,用自己的域名玩起来吧 ;)一键创建angular应用模版链接 :https://workbench.aliyun.com/application/front/create?fromConfig=24&fromRepo=sol_github_24参考文献:https://juejin.cn/post/6844903873212055560#heading-5【特别活动】阿里云云开发平台发布两年啦,感谢你对云开发长期的支持!快来分享你在云开发当中的开发感受,心得体会,以及成长经验吧!(包含但不仅限于文章、个人作品、毕业设计,企业应用等)。 所有在云开发平台做过相关应用开发的同学,分享并提交我与云开发的故事均可以获得优酷VIP月卡一张,我们会选择50个用户的故事和留言发放云开发优秀用户奖,前三名用户还将额外获得AirPods无线耳机一副,同时也会在相应的社区、媒体上对用户故事进行宣传。https://survey.aliyun.com/apps/zhiliao/EGhLta4n8?spm=a2c6h.activitiesspring.J_2503981410.4.2bc369feAz0051
为什么要使用ReactReact起源于Facebook内部项目,于2013年开源,由于出众的设计思想,以及更加专注于MVC当中的视图层, 使得创建交互式 UI 变得轻而易举。同时React还可以为你应用的每一个组件设计简洁的视图,当数据改变时 React 能有效地更新并正确地渲染组件。在写React时,就是在写每一个组件,最后将一个界面中的若干组件组合包装成完整页面,这样就实现了无论你现在正在使用什么技术栈,你都可以随时引入 React 来开发新特性,而不需要重写现有代码。跟随本教程,提升Web开发效率,了解如何轻松部署React,马上学习起来吧!一、搭建本地开发环境Create React App 是React 创建新的单页面应用的最佳方式。它会配置你的开发环境,以便使你能够使用最新的 JavaScript 特性,提供良好的开发体验,并为生产环境优化你的应用程序。你需要在你的机器上安装Node >= 14.0.0 。 要了解如何安装 Node.js,参阅nodejs.org。 如果你不确定系统中正在运行的 Node.js 版本是什么,请在终端窗口中运行node -v。npm 包管理器 因为我们通常都会使用react 提供的脚手架搭建项目结构,所以要下载并安装 npm 包并且npm >= 5.6,你需要一个 npm 包管理器。本指南使用 npm 客户端命令行界面,该界面默认安装在 Node.js。要检查你是否安装了 npm 客户端,请在终端窗口中运行 npm -v 。你可以使用 React CLI 来创建项目,生成应用和库代码,以及执行各种持续开发任务,比如测试、打包和部署。要使用 npm 命令安装 CLI,请打开终端/控制台窗口,输入如下命令:npm install -g create-react-app二、创建新的初始应用Create React App 不会处理后端逻辑或操纵数据库;它只是创建一个前端构建流水线,所以你可以使用它来配合任何你想使用的后端。它在内部使用Babel 和 webpack ,但是你无需了解他们的任何细节。要想创建一个新的初始应用项目,请执行:npx create-react-app my-app这里的 “my-app ” 是你需要创建的项目名称,自己定义。并且 npx 不是拼写错误—— 它是npm 5.2 + 附带的 package运行工具。项目目录介绍:README.md 项目说明 package.json、webpack 配置项目包管理。package-lock.json 锁定安装时的版本号,以保证其他人再npm install 时大家的依赖能保证一致。gitignore 这个是git的选择性上传的配置文件node_modules 项目的依赖包。public 公共文件,里边有公用模版和图标。src主要代码编写文件。public => mainifest.json : 移动端配置文件。src=> serviceWorker.js: 用于写移动端开发的,PWA必须用到这个文件,有了这个文件,就相当于有了离线浏览的功能。三、启动应用服务器进入工作区目录,并启动这个应用。cd my-app npm startnpm start命令会构建本应用、启动开发服务器、监听源文件,并且当那些文件发生变化时重新构建本应用,也会打开浏览器,并访问 localhost:3000 。你会发现本应用正运行在浏览器中。参考文献:https://zh-hans.reactjs.org/云平台一键部署React1.创建环境想要一键部署React,需要以下账号和服务:Github账号 (https://github.com/),阿里云账号,并使用阿里云账号登录云开发平台 (https://workbench.aliyun.com/),为保证最好的使用体验,请使用Chrome浏览器。开通OSS服务。未开通阿里云OSS的用户,点击链接 (https://workbench.aliyun.com/product/open?code=oss)开通OSS服务。OSS开通免费,有一定的免费额度,超过额度之后按量付费。2.创建React应用创建前端应用。打开快速开始 https://workbench.aliyun.com/app,找到React点击创建「创建应用」按钮。云资源访问授权。如果您之前没有使用过云开发平台,会出现云资源授权管理的选项,往下拉出现直至同意授权的字样,点击「同意授权」后出现授权成功,点击进入「下一步」。绑定Github账号。授权完成后选择来源仓库为Github,按照提示点击去绑定,绑定GitHub帐号,登录后并点击Authorize Aliyunworkben允许云开发平台构建、发布你的GitHub代码为可访问的网站。选择fork好的“React”代码仓库。选择第一步中的代码仓库,主干分支,并点击下一步。主干分支一般指的是代码的master或main等分支。填写基本信息,完成创建。填写基本信息并点击「完成」。成功后进入到应用详情和部署界面。3.在日常环境部署一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。 访问React网站。日常环境的测试域名也是可以访问的,点击访问已部署网站按钮会出现一个弹出,点击弹出上的立即访问就能够访问已经部署好的站点了。在部署完成后,可以继续本地编码,并将代码push到应用的“基本信息”中对应的代码仓库内。
前端开发离不开的Angular作为Google公司提供的一套开源的项目框架,Angular的模版功能强大且丰富,是一个比较完善的前端框架,包含服务,模版,数据双向绑定,模版化,路由,过滤器,自定义指令,依赖注入等所有功能,ng模块化比较大胆的引入了Java的一些东西(依赖注入),能够很容易的写出可复用的代码,对于敏捷开发的团队来说非常有帮助。同时该工具的核心功能就是对现有HTML编码以指令方式进行扩展,并使扩展后的HTML编码可以通过使用元素声明的方式来构建动态内容。因此,这样的扩展具有划时代的意义,这也是Angular框架自诞生起就备受关注的重要原因。在搭建Angular框架之前,让我们先来了解一下这个前端框架的特性和优势,首先Angular会把你的模版转换成代码,针对现代JavaScript虚拟机进行高度优化,轻松获得框架提供的高生产率,同时也可以借助新的组件路由器,Angular可以实现快速加载,自动代码拆分机制可以让用户仅仅加载那些用于渲染所请求页面的代码,提升速度与性能。了解了特性和性能,马上来看看如何具体进行前端框架搭建吧!一、搭建本地开发环境Node.jsAngular 需要 Node.js 的活跃 LTS 版或维护期 LTS版。关于具体版本需求,参阅 package.json 文件中的 engines。要了解如何安装 Node.js,参阅 nodejs.org。 如果你不确定系统中正在运行的 Node.js 版本是什么,请在终端窗口中运行 node -v。npm 包管理器Angular、Angular CLI 以及 Angular 应用都要依赖 npm 包来实现很多特性和功能。要下载并安装 npm 包,你需要一个 npm 包管理器。本指南使用 npm 客户端命令行界面,该界面默认安装在 Node.js。要检查你是否安装了 npm 客户端,请在终端窗口中运行 npm -v 。你可以使用 Angular CLI 来创建项目,生成应用和库代码,以及执行各种持续开发任务,比如测试、打包和部署。要使用 npm 命令安装 CLI,请打开终端/控制台窗口,输入如下命令:npm inatll -g @angula二、创建新的工作区和一个初始应用Angular 的工作区就是你开发应用所在的上下文环境。一个工作区包含一个或多个项目所需的文件。 每个项目都是一组由应用、库或端到端(e2e)测试组成的文件集合。 在本教程中,你将创建一个新的工作区。要想创建一个新的工作区和一个初始应用项目,需要:确保你现在没有位于 Angular 工作区的文件夹中。例如,如果你之前已经创建过 "快速上手" 工作区,请回到其父目录中。运行 CLI 命令 ng new,空间名请使用 angular-tour-of-heroes,如下所示:ng new angular-tour-of-heroesng new 命令会提示你输入要在初始应用项目中包含哪些特性,请按 Enter 或 Return 键接受其默认值。Angular CLI 会安装必要的 Angular npm 包和其它依赖项。这可能需要几分钟。它还会创建下列工作区和初始项目的文件:新的工作区,其根目录名叫 angular-tour-of-heroes。一个最初的骨架应用项目,位于 src/app 子目录下。相关的配置文件。初始应用项目是一个简单的 "欢迎" 应用,随时可以运行它。三、启动应用服务器进入工作区目录,并启动这个应用。cd angular-tour-of-heroes ng serve --openng serve 命令会构建本应用、启动开发服务器、监听源文件,并且当那些文件发生变化时重新构建本应用。--open 标志会打开浏览器,并访问 http://localhost:4200/。你会发现本应用正运行在浏览器中。参考文献:Angular官网云平台一键部署Angular作为Angular开发者的你,云开发平台为你提供了一站式,全云端的开发平台,让你可以打开浏览器就完成开发,调试,上线,同时云开发平台底层调用的是阿里云集团Serverless产品,可以实现低门槛开发,部署,调试,降低开发上手成本,让Angular应用可以一键快速部署!1.创建环境想要一键部署Angular,需要以下账号和服务:Github账号 (https://github.com/),阿里云账号,并使用阿里云账号登录云开发平台 (https://workbench.aliyun.com/),为保证最好的使用体验,请使用Chrome浏览器。开通OSS服务。未开通阿里云OSS的用户,点击链接 (https://workbench.aliyun.com/product/open?code=oss)开通OSS服务。OSS开通免费,有一定的免费额度,超过额度之后按量付费。2.创建Angular应用创建前端应用。打开快速开始 https://workbench.aliyun.com/app,找到Angular点击创建「创建应用」按钮。云资源访问授权。如果您之前没有使用过云开发平台,会出现云资源授权管理的选项,往下拉出现直至同意授权的字样,点击「同意授权」后出现授权成功,点击进入「下一步」。绑定Github账号。授权完成后选择来源仓库为Github,按照提示点击去绑定,绑定GitHub帐号,登录后并点击Authorize Aliyunworkben允许云开发平台构建、发布你的GitHub代码为可访问的网站。选择fork好的“Angular”代码仓库。选择第一步中的代码仓库,主干分支,并点击下一步。主干分支一般指的是代码的master或main等分支。填写基本信息,完成创建。填写基本信息并点击「完成」。成功后进入到应用详情和部署界面。3.在日常环境部署一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。访问Angular网站。日常环境的测试域名也是可以访问的,点击访问已部署网站按钮会出现一个弹出,点击弹出上的立即访问就能够访问已经部署好的站点了。在部署完成后,可以继续本地编码,并将代码push到应用的“基本信息”中对应的代码仓库内。
企业上云的数字化春风,夯实云上开发能力是关键随着一股春风袭来,新的一年“金三银四”求职季也悄然拉开了帷幕,想要在2022年的开发者求职大军中“脱颖而出”,了解企业的发展方向必不可少。在数字化的大潮下,要不要上云已经不再是一个问题,而是所有企业的必由之路,怎么上才是企业数字化的关键。突如其来的新冠疫情并未阻碍企业上云的进程,反而通过企业对数字化转型的重视大大促进了云原生技术的大规模落地。从以Docker为代表的容器技术兴起,再到Kubernetes(K8s)厚积薄发,在对性能、效率、可迁移性、可管理性以及便捷性永无止境的追求中,云原生不仅为企业提供了新的价值,同时使得开发者拥有了更加强大的工具以及新的应用构建与创新思维。云开发小课,助力开发者快速入门开发者如何利用这些工具,快速上云?一门好课是必不可少的“敲门砖”,云开发小课作为搭建云原生应用的基础课程,围绕云开发的能力解读、实践经验、以及真实案例推出9大课程,助力开发者们早日腾云驾雾,实现开发效率成倍增长!3月7日启程的“快速入门云开发,春暖花开迎offer”活动,将为踏入云端的开发者提供云开发入门免费课程,同时根据钉钉,支付宝,博客等实际应用场景,提供更多的实践开发案例,培养学习者从0到1了解“云开发”!活动入口:https://workbench.aliyun.com/activities/spring手把手教学和实践,还有上云奖励此次活动课程首先要打卡“云开发小课9大视频”,每门视频课均由阿里云技术专家亲自讲授,通过多年的开发经验,从前端与后端分离的应用配置,域名绑定,到链接RDS数据库等,为广大学习者讲述云开发当中的“门道”, 为“上云开发计划”铺设坚实的能力基础。“实践检验真知”,在学习了云开发小课课程之后,此次活动还为学习者安排了两个动手实践项目,通过开发部署一个静态Web网页可以帮助学习者快速了解如何使用云开发平台,以及如何快速托管一个静态网站。在“钉钉实践课程”中,可以动手实践开发钉钉会议室预定系统,了解钉钉应用架构形态,以及如何一键部署到云原生serverless计算服务当中。做到先人一步增加云开发实践经验!完成以上三个必选打卡任务,就可以领取小米电子秤啦!分享只属于你的故事,领取限量版冬奥云小宝一个人冥思苦想叫单机版学习者,多人学习交互才是最快掌握技能的方式,此次活动为开发者们搭建了钉钉交流空间,群内有各路牛人,同时还有服务人员在线解答活动问题。与大牛在线切磋“云开发”技法,在增长能力同时,积累人脉资源,为理想offer添砖加瓦!除了钉钉群内交流,云开发平台也将举办“我与云开发的故事”特别活动,每一位开发者都可以分享自己在云开发当中的开发感受,心得体验,以及成长经验(包括但不仅限于文章,个人作品,毕业设计,企业应用等),同时,每一位提交分享故事的用户均可获得“优酷vip”,本活动还将甄选50篇“云开发故事”,通过评选的用户可以获得“阿里云冬奥云小宝公仔”,前三名优秀“云开发故事”还可以额外获得“AirPods无线耳机一副“,马上和“云伙伴”共同在云开发的道路上披荆斩棘吧!此次活动依托云开发平台,可以一键搭建云服务实验场景,在线体验丰富的云开发模版库,通过学习视频和动手实践相结合,由浅入深地体验阿里云平台的高性能优势,快速上手云开发技术和Serverless!“不积跬步,无以至千里。不积小流,无以成江海”,云开发小课为开发者打开了这段精彩的旅程,云开发平台更会陪伴开发者的云技能成长,想要继续锤炼云技能的开发者们,可以尝试云开发模版库中的丰富案例,以及查看更多的云开发实战课程!活动入口:https://workbench.aliyun.com/activities/spring
错失冰墩墩,代码来补救!随着“2022北京冬奥会”的盛大开幕,冰墩墩的热度与日俱增,线上被抢空,线下排长龙,为购买冰墩墩苦恼的竟少了几根秀发!如今冬奥马上就要拉下帷幕,还没抢到“冰墩墩”不要紧,这款程序来帮忙,国内一位程序员dragonir用前端+建模的方式自己就实现了线上拥有“冰墩墩”,同时将代码开源到了GitHub上,不得不感叹科技的力量,程序员的伟大,让拥有“冰墩墩”变得如此简单!立体感的“冰墩墩”轻松实现这一部分是不需要用户去自己去码代码,是小编让大家了解一些具体的实现逻辑。云开发平台可以将原Github项目直接fork到自己的仓库上,马上来看一下“冰墩墩”技术如何实现的吧!1.引入资源首先引入开发页面所需要的库和外部资源,OrbitControls 用于镜头轨道控制、TWEEN 用于补间动画实现、GLTFLoader 用于加载 glb 或 gltf 格式的 3D 模型、以及一些其他模型、贴图等资源。import React from 'react'; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import bingdundunModel from './models/bingdundun.glb'; // ...2.页面DOM结构页面 DOM 结构非常简单,只有渲染 3D 元素的 #container 容器和显示加载进度的 .olympic_loading 元素。<div> <div id="container"></div> {this.state.loadingProcess === 100 ? '' : ( <div className="olympic_loading"> <div className="box">{this.state.loadingProcess} %</div> </div> )} </div>3.场景初始化初始化渲染容器、场景、相机。container = document.getElementById('container'); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; container.appendChild(renderer.domElement); scene = new THREE.Scene(); scene.background = new THREE.TextureLoader().load(skyTexture); camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 30, 100); camera.lookAt(new THREE.Vector3(0, 0, 0));4.添加光源本示例中主要添加了两种光源:DirectionalLight 用于产生阴影,调节页面亮度、AmbientLight 用于渲染环境氛围。// 直射光 const light = new THREE.DirectionalLight(0xffffff, 1); light.intensity = 1; light.position.set(16, 16, 8); light.castShadow = true; light.shadow.mapSize.width = 512 * 12; light.shadow.mapSize.height = 512 * 12; light.shadow.camera.top = 40; light.shadow.camera.bottom = -40; light.shadow.camera.left = -40; light.shadow.camera.right = 40; scene.add(light); // 环境光 const ambientLight = new THREE.AmbientLight(0xcfffff); ambientLight.intensity = 1; scene.add(ambientLight);5.加载进度管理使用 THREE.LoadingManager 管理页面模型加载进度,在它的回调函数中执行一些与加载进度相关的方法。本例中的页面加载进度就是在 onProgress 中完成的,当页面加载进度为 100% 时,执行 TWEEN 镜头补间动画。const manager = new THREE.LoadingManager(); manager.onStart = (url, loaded, total) => {}; manager.onLoad = () => { console.log('Loading complete!')}; manager.onProgress = (url, loaded, total) => { if (Math.floor(loaded / total * 100) === 100) { this.setState({ loadingProcess: Math.floor(loaded / total * 100) }); // 镜头补间动画 Animations.animateCamera(camera, controls, { x: 0, y: -1, z: 20 }, { x: 0, y: 0, z: 0 }, 3600, () => {}); } else { this.setState({ loadingProcess: Math.floor(loaded / total * 100) }); } };6.创建地面本示例中凹凸起伏的地面是使用 Blender 构建模型,然后导出 glb 格式加载创建的。当然也可以只使用 Three.js自带平面网格加凹凸贴图也可以实现类似的效果。使用 Blender 自建模型的优点在于可以自由可视化地调整地面的起伏效果。var loader = new THREE.GLTFLoader(manager); loader.load(landModel, function (mesh) { mesh.scene.traverse(function (child) { if (child.isMesh) { child.material.metalness = .1; child.material.roughness = .8; // 地面 if (child.name === 'Mesh_2') { child.material.metalness = .5; child.receiveShadow = true; } }); mesh.scene.rotation.y = Math.PI / 4; mesh.scene.position.set(15, -20, 0); mesh.scene.scale.set(.9, .9, .9); land = mesh.scene; scene.add(land); });7.添加“冰墩墩”在添加之前,需要在「室内设计网」找到冰墩墩的原始模型,并且需要在 Blender中转换模型格式,还需要在 Blender 中调整模型的贴图法线,才能还原渲染图效果透明塑料或玻璃质感实现代码:loader.load(bingdundunModel, mesh => { mesh.scene.traverse(child => { if (child.isMesh) { // 内部 if (child.name === 'oldtiger001') { child.material.metalness = .5 child.material.roughness = .8 } // 半透明外壳 if (child.name === 'oldtiger002') { child.material.transparent = true; child.material.opacity = .5 child.material.metalness = .2 child.material.roughness = 0 child.material.refractionRatio = 1 child.castShadow = true; } } }); mesh.scene.rotation.y = Math.PI / 24; mesh.scene.position.set(-8, -12, 0); mesh.scene.scale.set(24, 24, 24); scene.add(mesh.scene); });云平台一键部署“冰墩墩”1.创建环境想要一键部署“冰墩墩”,需要以下账号和服务:Github账号 (https://github.com/),阿里云账号,并使用阿里云账号登录云开发平台 (https://workbench.aliyun.com/),为保证最好的使用体验,请使用Chrome浏览器。开通OSS服务。未开通阿里云OSS的用户,点击链接 (https://workbench.aliyun.com/product/open?code=oss)开通OSS服务。OSS开通免费,有一定的免费额度,超过额度之后按量付费。2.创建“冰墩墩”应用创建前端应用。打开快速开始 https://workbench.aliyun.com/page/quickstart,点击创建「前端应用」按钮。云资源访问授权。如果您之前没有使用过云开发平台,会出现云资源授权管理的选项,往下拉出现直至同意授权的字样,点击「同意授权」后出现授权成功,点击进入「下一步」。绑定Github账号。授权完成后选择来源仓库为Github,按照提示点击去绑定,绑定GitHub帐号,登录后并点击Authorize Aliyunworkben允许云开发平台构建、发布你的GitHub代码为可访问的网站。选择fork好的“bingdundun”代码仓库。选择第一步中的代码仓库,主干分支,并点击下一步。主干分支一般指的是代码的master或main等分支。填写基本信息,完成创建。填写基本信息并点击「完成」。成功后进入到应用详情和部署界面。3.线上一键部署一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。收获线上“冰墩墩”一段段复杂的代码汇聚成了冬奥顶流“冰墩墩”,让每一位关注冬奥盛况的人都能够在此刻圆梦,感谢dragonir将代码开源到Github。点击图片可以立即前往云开发平台开始获取你的专属墩了。注:参考文章「牛批,「冰墩墩」代码,开源了!!」作者源代码地址:https://github.com/dragonir/3d/tree/master/src/containers/Olympic本文涉及奥运元素3D模型仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。
2022年03月
2022年02月