安装Vue
局部安装
局部安装并使用vue-cli 4.x版本,和创建项目
1 | # 先创建一个文件夹 |
全局安装
1 | npm install -g @vue/cli |
安装Element-UI
全局安装
1 | # 安装 |
1 | // main.js 使用 |
按需引入
1 | # 安装 |
1 | // main.js |
如果没有找到.babelirc文件,找找babel.config.js文件
一个一个import 按需引入有点麻烦,在根目录下面创建一个plugins文件夹,在这个文件夹下面创建element.js
1 | import Vue from 'vue' |
在main.js引入element.js
1 | import '../element.js' |
安装CSS预处理器
Sass
安装的是sass,使用的时候是scss…. 留意
1 | npm install --save-dev node-sass |
1 | # 注意这里是scss |
Less
1 | npm i less less-loader --save-dev |
1 | <style scoped lang="less"> |
重置样式
下面的代码可以搜索reset.css
在src/assets 下创建css文件夹,在这个文件下创建reset.css文件,粘贴
1 | /* http://meyerweb.com/eric/tools/css/reset/ |
在App.vue或者其他的组件中使用
1 | <style lang="scss"> |
安装图标库
1 | # 安装 |
安装axios
1 | npm i axios -S |
安装vue-router
1 | # vue-router3 和 4 的配置不一样 |
创建文件和文件夹
src->router->index.js
1 | import Vue from 'vue' |
路由懒加载和异步组件
1 | export default new Router({ |
setToken封装
src→utils→settoken.js
1 | export function setToken(key,token){ |
使用
1 | <script> |
配置代理服务器
1 | const { defineConfig } = require('@vue/cli-service') |
404
1 | { |
相关推荐

2024-02-26
Vue后台系统
项目由 Vue3 + Pina + JavaScript + Ant Design Vue 创建项目12345# 直接创建npm create vite@latest my-vue-app -- --template vue# 如果要创建ts或者自定义配置的项目就用这个npm create vite 引入Ant Design Vue1npm i --save [email protected] mian.js 删掉自带的style.css文件 12345678import {createApp} from 'vue'import Antd from 'ant-design-vue';import 'ant-design-vue/dist/reset.css';import App from './App.vue'const app = createApp(App);app.use(Antd)app.mount('#app'); 安装router1npm install vue-router@4 创建src/router/index.js文件 12345678910111213141516171819202122232425262728293031323334353637383940414243444546import { createWebHistory, createRouter } from 'vue-router'/** * Note: 路由配置项 * * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 * roles: ['admin', 'common'] // 访问路由的角色权限 * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 * meta : { noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false) title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 } */// 公共路由export const constantRoutes = [ { path: '/login', component: () => import('@/views/login'), hidden: true },]const router = createRouter({ history: createWebHistory(), routes: constantRoutes, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { top: 0 } } },});export default router; 配置开发环境编辑vite.config.js 123456789101112131415161718192021import { defineConfig, loadEnv } from 'vite'import path from 'path'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig(({ mode, command }) => { return { plugins: [vue()], resolve: { // https://cn.vitejs.dev/config/#resolve-alias alias: { // 设置路径 '~': path.resolve(__dirname, './'), // 设置别名 '@': path.resolve(__dirname, './src') }, // https://cn.vitejs.dev/config/#resolve-extensions extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] } }}) 项目根目录创建环境配置文件 .env.development .env.prod 12345678# 页面标题VITE_APP_TITLE = 若依管理系统# 开发环境配置VITE_APP_ENV = 'development'# 若依管理系统/开发环境VITE_APP_BASE_API = '/dev-api' 安装sass12345npm install -D sass# 直接使用就可以<style lang="scss" scoped></style> 全局样式创建src/style/index.scss文件 123456789101112131415161718@import './demo.scss';html { height: 100vh; box-sizing: border-box;}body { height: 100vh; margin: 0; //-moz-osx-font-smoothing: grayscale; //-webkit-font-smoothing: antialiased; //text-rendering: optimizeLegibility; //font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;}#app { height: 100vh;} 创建src/style/demo.scss文件 1// 这个文件是用来写css的,前面导入的这个文件,所以后面只需要写就行了 导入scss,编辑main.js 1import './style/index.scss' Ant Design Vue Icon的使用123456789101112131415<template> <a-form class="login-from"> <a-form-item size> <a-input placeholder="请输入用户名" size="large"> <template #prefix> <UserOutlined/> </template> </a-input> </a-form-item> </a-form></template><script setup> import {UserOutlined, LockOutlined} from '@ant-design/icons-vue'</script> 安装Axios1npm install axios 创建 src/utils/request.js 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556import axios from 'axios'axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'// 创建axios实例const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: import.meta.env.VITE_APP_BASE_API, // 超时 timeout: 10000})// request拦截器service.interceptors.request.use(config => { return config}, error => { console.log(error) Promise.reject(error)})// 响应拦截器service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; if (code === 401) { return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 500) { ElMessage({ message: msg, type: 'error' }) return Promise.reject(new Error(msg)) } else if (code === 601) { ElMessage({ message: msg, type: 'warning' }) return Promise.reject(new Error(msg)) } else if (code !== 200) { ElNotification.error({ title: msg }) return Promise.reject('error') } else { return Promise.resolve(res.data) } }, error => { console.log('err' + error) let { message } = error; if (message == "Network Error") { message = "后端接口连接异常"; } else if (message.includes("timeout")) { message = "系统接口请求超时"; } else if (message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; } ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) })export default service main.js 1import router from './router'

2022-11-19
Element-UI
表单验证简单版123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" ref="form"> <el-form-item label="用户名" prop="username" :rules="[ {required:true,message:'请输入用户名',trigger:'blur'}, {min:6,max:12,message:'长度在6-12位字符',trigger:'blur'}, ]"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password" :rules="[ {required:true,message:'请输入密码',trigger:'blur'}, {min:6,max:12,message:'长度在6-12位字符',trigger:'blur'}, ]"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>export default { data() { return { form: { username: "", password: "" } } }, methods:{ login(form){ this.$refs[form].validate((valid)=>{ if(valid){ console.log("验证通过") this.axios.post("http://localhost",this.form).then( res=>{ console.log(res) if(res.data.status === 200){ localStorage.setItem("username",res.data.username) this.$message({ message:res.data.message, type:'success' }) this.$router.push('/home') } } ) .catch(err=>{ console.error(err) }) }else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 自定义校验123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" :rules="rules" ref="form"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>export default { data() { // 验证用户名 const validateName = (rule, value, callback) => { if (value === '') { callback(new Error("请输入用户名")) } else if (value === '123456') { console.log("11111111111") callback(new Error('换个用户名,猜到啦')) } else { callback() } } const validatePassword = (rule, value, callback) => { if (value === '') { callback(new Error("请输入密码")) } else if (!value === '123456') { callback(new Error('换个密码,猜到啦')) } else { callback() } } return { form: { username: "", password: "" }, rules: { username: [{validator: validateName, trigger: 'blur'}], password: [{validator: validatePassword, trigger: 'blur'}], } } }, methods: { login(form) { this.$refs[form].validate((valid) => { if (valid) { console.log("验证通过") // this.axios.post("http://localhost", this.form).then( // res => { // console.log(res) // if (res.data.status === 200) { // localStorage.setItem("username", res.data.username) // this.$message({ // message: res.data.message, // type: 'success' // }) // this.$router.push('/home') // } // } // ) // .catch(err => { // console.error(err) // }) } else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 自定义校验封装新建一个vaildate.js 123456789101112131415161718192021// 用户名匹配export function nameRule(rule,value,callback){ if (value === '') { callback(new Error("请输入用户名")) } else if (value === '123456') { console.log("11111111111") callback(new Error('换个用户名,猜到啦')) } else { callback() }}export function passwordRule(rule,value,callback){ if (value === '') { callback(new Error("请输入密码")) } else if (value === '123456') { callback(new Error('换个密码,猜到啦')) } else { callback() }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" :rules="rules" ref="form"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>import {nameRule,passwordRule} from "@/utils/vaildate";export default { data() { return { form: { username: "", password: "" }, rules: { username: [{validator: nameRule, trigger: 'blur'}], password: [{validator: passwordRule, trigger: 'blur'}], } } }, methods: { login(form) { this.$refs[form].validate((valid) => { if (valid) { console.log("验证通过") // this.axios.post("http://localhost", this.form).then( // res => { // console.log(res) // if (res.data.status === 200) { // localStorage.setItem("username", res.data.username) // this.$message({ // message: res.data.message, // type: 'success' // }) // this.$router.push('/home') // } // } // ) // .catch(err => { // console.error(err) // }) } else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 遍历路由渲染菜单栏src>common>menu.vue 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869<template> <div class="menu"> <el-aside width="200px"> <el-menu router default-active="2" class="el-menu-vertical-demo" background-color="cornflowerblue" text-color="#fff" active-text-color="#ffd04b"> <template v-for="(item,index) in menus"> <div> <el-submenu :index="index + '' " :key="index" v-if="!item.hidden"> <template slot="title"> <i :class="item.iconClass"></i> <span>{{ item.name }}</span> </template> <el-menu-item-group v-for="(child,index) in item.children" :key="index"> <el-menu-item :index="child.path"> <i :class="child.iconClass"></i> {{ child.name }} </el-menu-item> </el-menu-item-group> </el-submenu> </div> </template> </el-menu> </el-aside> </div></template><script>export default { data(){ return{ menus:[] } }, created() { console.log(this.$router.options.routes) this.menus=this.$router.options.routes }, methods: { }}</script><style scoped lang="scss">.menu{ .el-aside{ height: 100%; .el-menu{ height: 100%; .fa{ margin-right: 10px; } } .el-submenu .el-menu-item{ min-width: 0; } }}</style> route.js 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136import Vue from 'vue'import Router from 'vue-router'// import Home from '../components/Home.vue'Vue.use(Router)export default new Router({ routes:[ { path:'/', // 重定向 redirect:'/login', // 路由懒加载 hidden:true, component:()=>import('@/components/Login.vue') // 异步组件 }, { path:'/login', name:'Login', hidden:true, // 路由懒加载 component:()=>import('@/components/Login.vue') // 异步组件 }, { path:'/home', // component:Home hidden:true, // 路由懒加载 component:()=>import('@/components/Home.vue') // 异步组件 }, { path:'/home2', // component:Home hidden:true, // 路由懒加载 component:resolve =>require(['@/components/Home.vue'],resolve) // 异步组件 }, { path:'*', hidden:true, // 路由懒加载 component:()=>import('@/components/404.vue') }, { path:"/home", name:"学生管理", iconClass:'fa fa-user', // 默认重定向 redirect:'/home/student', component:()=>import('@/components/Home.vue'), children:[ { path:'/home/student', name:'学生列表', iconClass:"fa fa-list", component:()=>import("@/components/students/StudentList.vue") }, { path:'/home/info', name:'信息列表', iconClass:"fa fa-list", component:()=>import("@/components/students/InfoList.vue") }, { path:'/home/infos', name:'信息管理', iconClass:"fa fa-list-alt", component:()=>import("@/components/students/InfoLists.vue") }, { path:'/home/work', name:'作业列表', iconClass:"fa fa-list-ul", component:()=>import("@/components/students/WorkList.vue") }, { path:'/home/workd', name:'作业管理', iconClass:"fa fa-th-list", component:()=>import("@/components/students/WorkMent.vue") } ] }, { path:"/home", name:"数据分析", iconClass:'fa fa-bar-chart', component:()=>import('@/components/Home.vue'), children:[ { path:'/home/dataview', name:'数据概览', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/DataView.vue") }, { path:'/home/mapview', name:'地图概览', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/MapView.vue") }, { path:'/home/travel', name:'信息管理', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/TraveMap.vue") }, { path:'/home/score', name:'分数地图', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/ScoreMap.vue") } ] }, { path:"/users", name:"用户中心", iconClass:'fa fa-user', component:()=>import('@/components/Home.vue'), children:[ { path:'/users/user', name:'权限管理', iconClass:"fa fa-user", component:()=>import("@/components/users/User.vue") } ] }, ], mode:'history'}) 面包屑的使用不同的路由,面包屑显示的信息会自动变化bread.vue 12345678910111213141516171819202122<template> <div> <el-card> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item v-for="(item,index) in $route.matched" :key="index" >{{item.name}}</el-breadcrumb-item> </el-breadcrumb> </el-card> </div></template><script>export default {}</script><style scoped></style> home.vue 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<template> <div class="home"> <Header></Header> <el-container class="content"> <Menu>menu</Menu> <el-container> <el-main> <Breadcrumb/> <div class="cont"> <router-view></router-view> </div> </el-main> <el-footer> <Footer></Footer> </el-footer> </el-container> </el-container> </div></template><script>import Header from "@/components/common/Header.vue";import Footer from "@/components/common/Footer.vue";import Menu from "@/components/common/Menu.vue";import Breadcrumb from '@/components/common/Breadcrumb.vue'export default { components: { Header, Footer, Breadcrumb, Menu, }, data() { return {} }}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped lang="less">.home { width: 100%; height: 100%; .content{ position: absolute; width: 100%; top: 60px; bottom: 0; .cont{ margin: 20px 0; } }}</style> Card 卡片12345<!-- body-style的使用 --><el-card :body-style="{padding:'0px'}"> <div style="padding-top: 4px;display: flex"> </div</div></el-card> 级联选择器新版本props的使用级联选择器太高可以在全局样式里给.el-cascader-panel设置高度为200px:props=”{ expandTrigger: ‘hover’, value: ‘cat_id’, label: ‘cat_name’, children: ‘children’ }” 多选框 1234567891011121314151617<div> <el-checkbox-group v-model="checkboxGroup1"> <el-checkbox-button v-for="city in cities" :label="city" :key="city">{{city}}</el-checkbox-button> </el-checkbox-group></div>const cityOptions = ['上海', '北京', '广州', '深圳']; export default { data () { return { checkboxGroup1: ['上海'], checkboxGroup2: ['上海'], checkboxGroup3: ['上海'], checkboxGroup4: ['上海'], cities: cityOptions }; } Avatar 头像12<el-avatar shape="square" size="small" src="https://baidu.com/logo"></el-avatar> Table 表格el-table怎样隐藏某一列el-table-column上添加v-if="false" 1234567891011121314151617181920212223<el-table v-loading="loading" :data="bczglList" @selection-change="handleSelectionChange"> <el-table-column label="id" align="center" prop="id" v-if="false" /> <el-table-column label="班次组编号" align="center" prop="bczbh" /> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['kqgl:bczgl:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['kqgl:bczgl:remove']" >删除</el-button> </template> </el-table-column></el-table> 先记着Vue同一个dom元素绑定多个点击事件如何绑定 1<el-button @click="dialogVisible = false;clearTempData()">取 消</el-button> 模板获取值 123<template slot-scope="scope"> <el-tag type="warning" v-for="ids in scope.row.typeID.split(',')">{{getGameType(ids)}}</el-tag></template> Store 数据 1agentId: _this.$store.getters.name.agentId,

2022-11-19
Vue
按照官方文档的教程步骤来,大部分的代码Demo,都是通过脚手架创建的项目的基础上创建的,用的版本的Vue3,Typescript,组合式的写法 基础创建一个应用多个应用实例 没什么用,现在项目都是从头开始都是vue项目,不会用来一步一步替换,没有想到应用场景 1234567891011121314151617181920212223242526272829303132333435363738394041424344<!DOCTYPE html><html><head> <title>Vue Multiple Instances Example</title> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script></head><body> <div id="app1"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <div id="app2"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <script> var app1 = new Vue({ el: '#app1', data: { message: '这是第一个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第一个实例的消息已更新'; } } }); var app2 = new Vue({ el: '#app2', data: { message: '这是第二个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第二个实例的消息已更新'; } } }); </script></body></html> 模板语法文本插值 双大括号标签会被替换为msg 属性的值。同时每次 msg 属性更改时它也会同步更新。 12345678<script setup lang="ts">import {ref} from "vue";const text = ref<string>("文本插值语法");</script><template> {{text}}</template> 原始 HTML 有的时候想想渲染源码,有的时候不想渲染源码 v-html就是指令语法 123456789<script setup lang="ts">import {ref} from "vue";const rawHtml = ref<string>("<span style='color: red'>This should be red.</span>");</script><template> <p>Using text interpolation: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p></template> Attribute 绑定 如果想给html的属性绑定数据要怎么操作呢,下面以给div的id和class属性绑定做演示 123456789101112131415161718192021222324252627282930313233343536373839404142<script setup lang="ts">import {ref} from "vue";const myId = ref<string>("myId");const showButton = ref<boolean>(false); type objectOfAttrs={ id: string, class:string} const objectOfAttrsObj=ref<objectOfAttrs>({ id: "myId4", class:"demo"}); </script><template> <!-- ❎错误做法 --> <div id={{myId}}></div> <!-- ✅正确做法,如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。 --> <div v-bind:id="myId"></div> <!-- ✅正确做法,简写写法 --> <div :id="myId"></div> <!-- ✅正确做法,同名简写写法,就是属性(Id),和等号对应的值是一样的话 --> <div :id></div> <!-- ✅正确做法,同名简写写法,第二种写法 --> <div v-bind:id></div> <!-- -------------------------------------------------------- --> <!-- 绑定布尔类型 --> <button :disabled="showButton">按钮</button> <!-- 动态绑定多个值 --> <div v-bind="objectOfAttrsObj">动态绑定多个值,只是需要写v-bind</div></template> 响应式基础这个有点底层,不去了解了,大概意思是简单的数据类型就用ref,对象或者复杂嵌套的结构就用reactive 计算属性基础示例 Vue 的计算属性会自动追踪响应式依赖。它会检测到 authorComputed 依赖于 author.books,所以当 author.books 改变时,任何依赖于 authorComputed 的绑定都会同时更新。 计算属性会被缓存,并且只调用一次,方法是调用一次执行一次,在例子中,触发了一次页面渲染(页面的数据有变动),方法就会被执行一次 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<script setup lang="ts">import {computed, reactive, ref} from "vue";type authorType={ name:string, books:string[]}const author = reactive<authorType>({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ]})// 计算属性,使用泛型,返回值必须是字符串const authorComputed = computed<string>(()=>{ return author.books.length >0?author.books.length.toString():"没有书"})function add(){ author.books.push("Vue 5 - Advanced Guide")}// 计算属性,使用泛型,返回值必须是字符串const myDateTimeComputed = computed<number>(() => { return getDateTime();});function getDateTime(){ console.log("方法被调用") return Date.now();}function myDateTimeMethod(){ return getDateTime();}// 定义一个用于触发重新渲染的状态const renderCounter = ref(0);function forceRerender() { console.log("12321") renderCounter.value++;}</script><template> <!-- 计算属性 --> <span>通过计算属性计算出来的结果:{{ authorComputed }}</span> <br> <button @click="add()">添加数据</button> <br> <!-- 计算属性和方法的区别 --> <button @click="forceRerender">触发重新渲染</button> 计算属性和方法的区别-计算属性:{{ myDateTimeComputed }} 计算属性和方法的区别-方法:{{renderCounter}}-{{ myDateTimeMethod() }}</template> 可写计算属性 计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建 1234567891011121314151617181920212223242526272829303132333435363738394041<script setup lang="ts">import {computed, reactive, ref} from "vue";const firstName = ref('Anthony')/** * 错误的写法 */// const fullName = computed(()=>{// return firstName.value + ' ' + lastName.value// })/** * 正确的写法 */const fullName = computed({ // getter get() { return firstName.value }, // setter set(newValue) { // 注意:我们这里使用的是解构赋值语法 firstName.value = newValue }})/** * 这样是不对的 */function showWarn(){ console.log("修改计算属性") fullName.value="DiDiDi"}</script><template> 演示修改计算属性,控制台报错:{{fullName}} <button @click="showWarn">尝试修改计算属性</button></template> Class 与 Style 绑定太麻烦了,不看了 条件渲染v-if 可以单独使用 v-else-if 要跟v-if 一起使用 v-else 要跟v-if 或者 v-else-if 一起使用 12345678910111213<script setup lang="ts">import {computed, reactive, ref} from "vue";const season = ref<number>(6);</script><template> <h1 v-if="season >=1 && season <=3">春季</h1> <h1 v-else-if="season >=4 && season <=6">不是春季</h1> <h1 v-else>数据错误</h1></template> 列表渲染123456789101112131415161718192021222324<script setup lang="ts">import {computed, reactive, ref} from "vue";const items = ref([{ message: 'Foo' }, { message: 'Bar' }])</script><template> for循环 <li v-for="(item,index) in items"> {{index}}-{{ item.message }} </li> 解构函数 <li v-for="{ message } in items"> {{ message }} </li> 解构函数,有索引的时候 <li v-for="({message},index) in items"> {{index}}-{{ message }} </li></template> 事件处理我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler" 或 @click="handler"。 内联事件处理器 12345678910<script setup lang="ts">import {computed, reactive, ref} from "vue";const count = ref(0)</script><template> <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p></template> 方法事件处理器 1234567891011<script setup lang="ts">function greet() { console.log("方法事件处理器")}</script><template> <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button></template> 表单输入绑定主要是看框架了,先不学了,太多细节了 生命周期钩子这个API好多个,以后慢慢看 侦听器每次修改被监听的对象,就会打印旧值和新值 基本示例 12345678910111213141516<script setup lang="ts">import {ref, watch} from 'vue'/*被监听的对象*/const question = ref<number>(1);// 可以直接侦听一个 refwatch(question, async (newQuestion, oldQuestion) => { console.log("老值:", oldQuestion) console.log("新值:", newQuestion)})</script><template> <input v-model="question"/></template> 侦听不同的数据源类型 watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455<script setup lang="ts">import {reactive, ref, watch} from 'vue'const x = ref<number>(0)const y = ref<number>(0)type authorType={ name: string,}const z = reactive<authorType>({ name:"anthony"})// refwatch(x, (newX,oldValue) => { console.log(`监听单个ref,x的旧值是:${oldValue},新值是:${newX}`)})// getter 函数watch( () => x.value + y.value, (newSum, oldSum) => { console.log(`监听getter函数, 原来的和值是:${oldSum},新值是: ${newSum}`) })// 多个来源组成的数组watch([x, () => y.value], ([newX, newY],[oldX, oldY]) => { console.log(`监听多个数据源:X的旧值是:${oldX},X的新值是:${newX},Y的旧值是:${oldY},Y的新值是:${newY}`)})// 监听reactive// 错误,因为 watch() 得到的参数是一个 number// 错误的写法,会提示报错,// MyClick.vue:35 [Vue warn]: Invalid watch source: anthony A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.// at <MyClick>// at <App>// watch(z.name, (count) => {// console.log(`name is: ${count}`)// })// 正确的写法watch( () => z.name, (nameNew,oldName) => { console.log(`name is: ${nameNew},${oldName}`) })</script><template> <input v-model="x" type="number"/> <input v-model="y" type="number"/> <input v-model="z.name" /></template> 模板引用进入到页面会自动聚焦到输入框中 123456789101112131415<script setup>import { ref, onMounted } from 'vue'// 声明一个 ref 来存放该元素的引用// 必须和模板里的 ref 同名const input = ref(null)onMounted(() => { input.value.focus()})</script><template> <input ref="input" /></template> 组件基础定义组件 123456789<script setup>import { ref } from 'vue'const count = ref(0)</script><template> <button @click="count++">You clicked me {{ count }} times.</button></template> 使用组件 在父组件中引入 1234567<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick/></template> 传递 props(父传子) 1234567891011121314151617181920212223// 子组件<script setup lang="ts">import {ref} from 'vue'const count = ref<number>(0)defineProps(['title'])</script><template> <button @click="count++">{{title}},You clicked me {{ count }} times.</button></template>// 父组件<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick title="计算器1"/> <MyClick title="计算器2"/> <MyClick title="计算器3"/> <MyClick title="计算器4"/></template> emit(子传父) 父组件 123456789101112131415161718192021222324<script setup lang="ts">import { ref } from 'vue'import MyEmit from "@/components/MyEmit.vue";type Person = { name: string, age: number}const myName = ref<string>('')const myAge = ref<number>('')const getSubmit = (data: Person) => { myName.value = data.name myAge.value = data.age}</script><template> <MyEmit @myEmit="getSubmit" /> 从子组件接收的数据:{{ myName }} {{ myAge }}</template> 子组件 123456789101112131415<script setup lang="ts">type Person = { name: string, age: number}const me = { name: 'anthony', age: 23}const emit = defineEmits<{ (e: 'myEmit', payload: Person): void }>();emit('myEmit', me)</script> $bus 消息总线main.js 123456789101112131415//引入Vueimport Vue from 'vue'//引入Appimport App from './App.vue'//关闭Vue的生产提示Vue.config.productionTip = false//创建vmnew Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线 },}) App.vue 1234567891011121314151617181920212223242526272829<template> <div class="app"> <h1>{{msg}}</h1> <School/> <Student/> </div></template><script> import Student from './components/Student' import School from './components/School' export default { name:'App', components:{School,Student}, data() { return { msg:'你好啊!', } } }</script><style scoped> .app{ background-color: gray; padding: 5px; }</style> Student.vue 12345678910111213141516171819202122232425262728293031323334<template> <div class="school"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div></template><script> export default { name:'School', data() { return { name:'尚硅谷', address:'北京', } }, mounted() { // console.log('School',this) this.$bus.$on('hello',(data)=>{ console.log('我是School组件,收到了数据',data) }) }, beforeDestroy() { this.$bus.$off('hello') }, }</script><style scoped> .school{ background-color: skyblue; padding: 5px; }</style> School.vue 1234567891011121314151617181920212223242526272829303132<template> <div class="student"> <h2>学生姓名:{{name}}</h2> <h2>学生性别:{{sex}}</h2> <button @click="sendStudentName">把学生名给School组件</button> </div></template><script> export default { name:'Student', data() { return { name:'张三', sex:'男', } }, methods: { sendStudentName(){ this.$bus.$emit('hello',this.name) } }, }</script><style lang="less" scoped> .student{ background-color: pink; padding: 5px; margin-top: 30px; }</style> $set$nextTick深入组件插槽插槽内容与出口 主文件 1234567891011121314151617181920212223242526272829<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";import MySlotWithName from "@/components/MySlotWithName.vue";import MySlotWithOutName from "@/components/MySlotWithOutName.vue";</script><template> <h1>插槽演示</h1> <!-- 匿名插槽 --> <MySlotWithOutName> <a href="https://baidu.com">跳转到baidu</a> </MySlotWithOutName> <br> <MySlotWithName> <!-- 具名插槽 --> <!-- <template v-slot:url> --> <!-- 简写的方式 --> <template #url> <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> MySlotWithOutName.vue 1234<template> 匿名插槽 <slot /></template> 1234<template> 具名插槽 <slot name="url" /></template> 作用域插槽 12345678910111213<script setup lang="ts">import MySlotWithName from "@/components/MySlotWithName.vue";</script><template> <MySlotWithName> <template #url="data"> {{ data.title }}- {{ data.age }} <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> 1234<template> 具名插槽 <slot name="url" title="anthony" age="12" /></template> 依赖注入给所有的下级组件,包括直接子组件传递数据和方法 这个例子的最上层组件修改数据之后,可以修改所有组件显示的数据 这个例子的下层组件修改数据之后,也是可以修改所有组件显示的数据 最上层组件 12345678910111213141516171819202122232425262728293031323334353637<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";type Person = { name: string, age: number}const me: Person = reactive({ name: 'anthony', age: 23})provide("provideDemo", me)const update = () => { me.age = me.age + 1 console.log(me);}const updateTwo = () => { me.age = me.age + 2 console.log(me);}provide("provideUpdateTwo", updateTwo)</script><template> 最顶层组件,接收到的数据:{{ me.name }},{{ me.age }}<button @click="update">修改所有层数据+1</button> <br> <First /></template> 第一层组件 1234567891011121314151617<script setup lang="ts">import { inject } from 'vue'import Second from "./Second.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第一层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Second /></template> 第二层组件 12345678910111213141516<script setup lang="ts">import { inject } from 'vue'import Third from "./Third.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第二层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Third /></template> 第三层组件 123456789101112131415<script setup lang="ts">import { inject } from 'vue'type Person = { name: string, age: number}const me = inject<Person>("provideDemo")const provideUpdateTwo = inject("provideUpdateTwo")</script><template> 这是第三层,接收到的数据:{{ me.name }},{{ me.age }}<button @click="provideUpdateTwo">修改最顶层数据+2</button></template> yarnyarn的安装 1npm install -g yarn yarn常用命令 12345678910111213141516// 初始化yarn init // 添加包yarn add [package]yarn add [package]@[version]yarn add [package]@[tag]// 添加到不同依赖项yarn add [package] --devyarn add [package] --peeryarn add [package] --optional// 升级包yarn upgrade [package]// 移除依赖包yarn remove [package]// 安装所有依赖yarn 或 yarn install 报错1.node新版本引起的报错 1234this[kHandle] = new _Hash(algorithm, xofLen);^Error: error:0308010C:digital envelope routines::unsupported 解决方法1: 123456推荐:修改package.json,在相关构建命令之前加入SET NODE_OPTIONS=--openssl-legacy-provider"scripts": { "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"}, 解决方法2: 降低node版本 前端Vue父组件点击按钮打开子组件弹窗案例父组件 12345678910111213141516171819202122232425<template> <div> # 需要定义ref=child <indexChild ref="child"></indexChild> <el-button @click="open">打开弹窗</el-button> </div></template><script>import indexChild from "../../components/indexChild.vue";export default { components: { indexChild }, data () { return { }; }, methods: { open () { this.$refs.child.open(); // 这样可以直接访问子组件方法,用ref拿子组件方法 } }}</script> 子组件 123456789101112131415161718192021222324252627<template> <div> <el-dialog title="收货地址" :visible.sync="dialogFormVisible"> <span>这是一段信息</span> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button> </div> </el-dialog> </div></template><script>export default { data () { return { dialogFormVisible: false, }; }, methods: { open () { // 在父组件调用打开 this.dialogFormVisible = true } }};</script> 参考:前端Vue父组件点击按钮打开子组件弹窗案例

2022-11-19
Python
安装虚拟环境12345678910111213141516171819202122232425262728293031323334# 判断有没有虚拟环境virtualenv -V# 安装虚拟环境# 需要sudopip install virtualenvpip install virtualenvwrapper# 查看有多少虚拟环境的文件夹workon# 创建虚拟环境文件夹mkvirtualenv 文件夹名字# 从虚拟文件夹退出deactiave# 进入虚拟环境中workon 虚拟环境名称# 删除虚拟环境rmvirutalenv# 查看虚拟环境有哪些框架,要在虚拟环境中执行pip freeze# 安装软件指定版本,要在虚拟环境中执行pip install flask==10.0.0.0# 导出虚拟环境中的所有扩展,要在虚拟环境中执行pip freeze > requirements.txt# 安装,要在虚拟环境中执行pip install -r requirements.txt centos7安装python3特别是在喜欢环境中已经安装的python2.x的版本中 1234567891011121314151617181920212223242526272829# 这个可能不一定要装sudo yum -y groupinstall "Development tools"# 需要的sudo yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel# 下载安装包wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0a1.tar.xz# 解压tar -xvxf Python-3.7.0a1.tar.xz# 复制文件夹mv Python-3.7.0 /usr/local# 进入到文件夹cd /usr/local/Python-3.7.0/# 编译,一定需要后面的参数./configure --prefix=/usr/local/bin/python3make & make install# 添加软连接ln -s /usr/local/bin/python3/bin/python3 /usr/bin/python3ln -s /usr/local/bin/python3/bin/pip3 /usr/bin/pip3# 验证python3pip3 基础1.注释123456789# 注释后面需要一个空格print("单行注释")print("单行注释") # 单行注释和代码之间至少要有两个空格"""多行注释"""print("这是多行注释") 2.算数运算符乘法的使用,用*可以拼接字符串 12345In [1]: "A" * 30Out[1]: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'In [2]: 1 * 30Out[2]: 30 优先级 幂 >* > / > % > // >+ > - 3.变量3.1.变量的类型数字型 整数 int 浮点 float(计算要小心) 布尔 布尔值可以用 and、or 和 not 运算 复数(用于科学技术的) 非数字型 String(字符串) List(列表) Tuple(元组) Dictionary(字典) 3.2.type函数12345678# 整数print(type(1))# 浮点数print(type(1.5))# 字符串print(type("hello world"))# 空值print(type(None)) 3.3.不同类型的变量之间的计算1234567891011121314151617181920212223242526272829In [10]: age =13In [11]: sex =TrueIn [12]: height = 180.1In [13]: age + sexOut[13]: 14In [14]: age + heightOut[14]: 193.1In [15]: age + heightOut[15]: 193.1# 字符串的拼接In [17]: first_name ="东尼"In [18]: last_name="安"In [19]: last_name+first_nameOut[19]: '安东尼'# 字符串不能和数字相加In [20]: last_name + 10---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-20-45feb354f2d0> in <module>----> 1 last_name + 10TypeError: can only concatenate str (not "int") to str 3.4.变量的输入12345678910# 可以不需要参数In [24]: input()123# 加参数,做提示In [27]: pwd = input("输入数字")输入数字123In [28]: pwd + "456"Out[28]: '123456' 3.5.数据类型转换 方法名 含义 str() 转成字符串 float() 转成浮点 int() 转成int 123456789101112131415161718192021222324# 数字和字符串相互转In [3]: age = 23In [4]: type(age)Out[4]: intIn [5]: age_str = str(age)In [6]: type(age_str)Out[6]: strIn [7]: type(int(age_str))Out[7]: int# 浮点转数值,缺失精度In [9]: pi = "3.14"In [10]: piOut[10]: '3.14'In [13]: type(float(pi))Out[13]: floatIn [15]: int(float(pi))Out[15]: 3 3.6.变量的格式化输出 符号 描述 %c 格式化字符(输出数值对应的ASCII码) %s 格式化字符串 %d 格式化整数(%06d,不足的补0) %x 格式化十六进制数(小写) %X 格式化十六进制数(大写) %o 格式化八进制数 %f 格式化浮点数字,可以指定小数点精度(%.2f) %% 输出%号 12345678910"""'我的名字是:anthony,请多多关照我的学号是:000001,请多多关照单价是:1.00,购买了2.00斤,总价是:2.000数据的比例是:20%"""print("我的名字是:%s,请多多关照" % "anthony")print("我的学号是:%06d,请多多关照" % 1)print("单价是:%.2f,购买了%.2f斤,总价是:%.3f" % (1,2,2))print("数据的比例是:%02d%%" % 20) 3.7.变量的命名 字母,和下划线和数字组成 不能以数字开始 不能与关键字重名 其余的符号都不行 区分大小写 =左右都要添加空格 两个单词之间用_ 4.条件控制4.1.if12345678910age = 15if age >= 18: print("成年了") print("在一个缩进的,是一个代码块")elif age<=18: print("没有成年")else: print("输入错误")print("结束了") 4.2.逻辑运算符123print(True and False)print(True or False)print(not True) 4.3.随机数1234In [17]: import randomIn [18]: random.randint(12,20)Out[18]: 12 4.3.while1234i = 0while i < 5: print("....") i += 1 4.4.continue 和 break如果是嵌套循环,用这个两个关键字,也只是结束当前的循环,不会影响外层的循环 5.函数5.1.函数注释1234# 这个也是可以注释的def test(): """这个也是可以注释的""" print("打印乘法表") 5.2.函数调用index.py 12def chengfabiao(): print("打印乘法表") test.py 12import indexindex.chengfabiao() 5.3.局部方法修改全局变量12345678910num =10def mo(): # 声明num是全部变量 global num num=100 print(num)mo()print(num) 5.4.多个返回值12345678def change(x,y): return y,xx =1y=2x,y = change(1,2)print(x)print(y) 5.5.缺省函数1234567891011121314151617def measure(age,gender=1): print(age) print(gender)def measure2(age,gender=1,name="anthony"): print(age) print(gender) print(name)measure(1)measure(1,2)# 有多个缺省的时候,需要指定参数名称measure2(1,name="anthonyyang",gender=2)# 拆包measure(1,*(2,3,4),**{"name":"anthony"}) 5.6.多值参数习惯存元祖的使用用*args,存字典的时候用**args 5.7.函数的参数-默认参数1234567891011121314def my_print(name,age): print(name,age)def my_print2(name,age=13): print(name,age)# 有默认值的形参,不能放在前面,会报错# def my_print3(age=13,name):# print(name,age)my_print("anthony",12)my_print2("anthony2",123)my_print2("anthony2") 5.8.函数的参数-关键字参数12345678910111213141516def my_print(name,address): print(name,address)my_print("anthony","广东")my_print("广东","anthony")my_print(name="anthony",address="广东")my_print(address="广东",name="anthony",)# ----------------**kw是关键字参数,且 hobby 就是一个 dict (字典)-------------def my_print2(name,address,**kw): if 'age' in kw: print("age=",kw["age"])my_print2("anthony","广东",age=123)my_print2("anthony","广东",kw={"age":123}) 5.8.函数的参数-只接受关键字参数123456789def my_print(name,*,address): print(name,address)# 报错# my_print("anthony","广东")# my_print("广东","anthony")my_print("anthony",address="广东")my_print(address="广东",name="anthony") 5.8.函数的参数-不定长参数hobby是可变参数,且 hobby 其实就是一个 tuple (元祖) 12345678910def print_user_info( name , age , sex = '男' , * hobby): # 打印用户信息 print('昵称:{}'.format(name) , end = ' ') print('年龄:{}'.format(age) , end = ' ') print('性别:{}'.format(sex) ,end = ' ' ) print('爱好:{}'.format(hobby)) return;# 调用 print_user_info 函数print_user_info( '两点水' ,18 , '女', '打篮球','打羽毛球','跑步 6.容器6.1.列表虽然列表可以存储不同类型的数据,但是在开发中,存储的都是相同类型数据,因为要迭代 123456789101112131415161718192021222324mylist=["a","b","c"]print(mylist)# 通过索引,访问列表中的值print(mylist[1])# 通过方括号的形式来截取列表中的数据,访问列表中的值# 就是从第 0 个开始取,取到第 2 个,但是不包含第 2 个print(mylist[0:2])# 通过索引对列表的数据项进行修改或更新mylist[1] = "bb"print(mylist)# 使用 append() 方法来添加列表项mylist.append("d")print(mylist)# 使用 del 语句来删除列表的的元素del mylist[3]print(mylist)# 列表长度print(len(mylist)) 6.2.元祖元祖用的是括号 与列表比较,元祖元素不能修改 123456789101112131415161718192021222324252627# 创建元祖方法1tuple1=('两点水','twowter','liangdianshui',123,456)tuple2='两点水','twowter','liangdianshui',123,456# 创建元祖方法2tuple3 = ()# 创建元祖方法3tuple4 = (123,)print(tuple1)print(tuple2)print(tuple3)print(tuple4)# 访问元祖print(tuple1[1])# 修改元祖的值mylist=[1,2,3]tuple5=("ddd",mylist)print(tuple5)mylist[1]=43print(tuple5)# 删除元祖,tuple 元组中的元素值是不允许删除的,但我们可以使用 del 语句来删除整个元组del tuple1 元祖和列表相互转换 123456789101112131415# 声明元祖In [54]: num_list = (1,2,3,4)In [55]: type(num_list)Out[55]: tuple# 元祖转成列表In [56]: my_list = list(num_list)# 修改值In [57]: my_list[0]=5# 再转成元祖In [58]: print(tuple(my_list))(5, 2, 3, 4) 6.2.字典列表是有序的 字典是无序的 12345678910111213141516171819202122232425262728names={"name":"xiaoming","age":"23"}# 取值print(names["name"])# 新增和修改(key存在,就是新增,不存在就是修改)names["address"] ="feilvb"names["name"] ="anthony123"print(names)# 删除names.pop("name")print(names)# 统计键值对的数量print(len(names))# 合并键值对,如果合并的时候有相同的key,那个value就是更新值temp = {"a":"b"}names.update(temp)print(names)# 遍历字典for k in names: print("遍历",k,names[k])# 清空字典names.clear() 6.3.setset可以理解为只有key的字典 12345678# 创建setset1 = set([1,2,3])# 添加元素set1.add(200)# 删除元素set1.remove(1) 6.4.字符串123456789101112131415str ="hello hello"print("字符串长度",len(str))print("字符串出现次数",str.count("llo"))print("取索引",str.index("llo"))print("取值",str[1])# 换行符,都是空白字符print("判断空白字符",str.isspace())print("是否以指定字符串开始",str.startswith("hello"))print("是否以指定字符串结束",str.endswith("LLO"))print("查找指定字符串",str.find("llo"))print("替换字符串",str.replace("hello","HELLO"))print(str[0:9:2])# bytes转字符串print(b"abcde".decode("utf-8")) 字符串前加 b:b 前缀代表的就是bytes 字符串前加 r:r/R:非转义的原始字符串 7.公共方法 内置函数: len max 只能比较字典的key min 只能比较字典的key 2.字符串,列表,元祖都可以切片 3.查看地址值 1id(str) 面向对象类名需要大驼峰命名法 1.基本语法1.1.创建对象1234567891011class Cat: def eat(self): print("小猫爱吃鱼") def drink(self): print("小猫爱喝水")tom = Cat()tom.eat()tom.drink() 1.2.对象内置方法(魔术方法)123456789101112131415161718192021222324252627class Cat: # 构造方法 def __init__(self,name): print("初始化方法") self.name=name # 成员方法 def eat(self): print(self.name+"爱吃鱼") # 成员方法 def drink(self): print(self.name+"爱喝水") # 魔术方法 def __del__(self): print("销毁方法") # 魔术方法 def __str__(self): return "重写tostring"tom = Cat("Tom")tom.eat()tom.drink()print(tom) 1.3.私有属性和方法1234567891011121314151617181920212223242526272829303132333435class Cat: # 构造方法 def __init__(self,name): print("初始化方法") self.name=name self.__age =18 def eat(self): print(self.name+"爱吃鱼") def drink(self): print(self.name+"爱喝水") def say_age(self): print("年纪是:"+str(self.__age)) # 调用私有方法 self.__private_method() def __private_method(self): print("私有方法") def __del__(self): print("销毁方法") def __str__(self): return "重写tostring"tom = Cat("Tom")tom.eat()tom.drink()tom.say_age()print(tom)# 这种访问方式,也是可以访问到私有的属性和方法的print(tom._Cat__age) 1.4.继承和重写123456789101112131415161718192021222324252627282930class Animal: def __init__(self): self.name1 =100 self.__num2 = 200 def eat(self): print("动物吃") def run(self): print("动物跑") # 子类不允许调用私有方法 def __test(self): print("父类可以访问到私有属性和私有方法")class Dog(Animal): def run(self): print("子类打印,开始调用父类方法") super().run() print("调用完父类方法")# animal = Animal()# animal.eat()# animal.run()dog = Dog()dog.eat()dog.run() 1.5.多继承尽量避免使用多继承,如果继承了两个累,两个类有相同的方法和属性,容易混淆 123456789101112131415161718192021222324252627282930class Animal: def __init__(self): self.name1 = 100 self.__num2 = 200 def eat(self): print("动物吃") def run(self): print("动物跑") # 子类不允许调用私有方法 def __test(self): print("父类可以访问到私有属性和私有方法")class Zoo: def eat(self): print("动物园吃饭")class Dog(Animal, Zoo): def run(self): print("子类打印,开始调用父类方法") super().run() print("调用完父类方法")dog = Dog()dog.eat() 1.6.多态12345678910111213141516171819202122232425262728class Dog(object): def __init__(self,name): self.name = name def game(self): print("蹦蹦跳跳",self.name)class Xiaotianquan(Dog): def game(self): print("哮天犬",self.name)class Person(object): def __init__(self,name): self.name = name def game_with_dog(self,dog): print("人和狗玩耍",self.name,dog.name) dog.game()# dog = Dog("旺财")dog = Xiaotianquan("旺财")xiaoming = Person("xiaoming")xiaoming.game_with_dog(dog) 1.7.类属性和类方法和静态方法类属性 相当于静态变量 123456class Dog(object): # 类属性 age = 12 def __init__(self,name): self.name = name 类方法 123456789101112class Dog(object): # 类属性 age = 12 # 类方法 @classmethod def show_age(cls): print("静态方法",cls.age)dog = Dog()Dog.show_age() 静态方法,在不用方法类属性和静态属性的时候,可以定义成静态方法 1234567891011121314151617181920class Dog(object): # 类属性 age = 12 # 类方法 @classmethod def show_age(cls): print("类方法",cls.age) @staticmethod def static_method(): print("静态方法")dog = Dog()# 调用类方法Dog.show_age()# 调用静态方法Dog.static_method() 2.异常2.1.异常的完整语法1234567891011try: num = int(input("输入一个整数:")) 10 / numexcept ZeroDivisionError: print("请不要输入数字0")except Exception as result: print("未知错误 %s" % result)else: print("没有异常才会执行的代码")finally: print("无论是否有异常,都会异常的代码") 2.2.主动抛异常123456789101112def check(name): if(name == "anthony"): return "是安东尼" else: # 主动抛异常 raise Exception("不是安东尼")# 捕获异常try: print(check("anthony2"))except Exception as result: print(result) 3.模块导入的语法如下:[from 模块名】import [模块 1类1变量1函数1x[as别名]常用的组合形式如: import 模块名 from 模块名 import 类、变量、方法等 from 模块名 import * import 模块名 as 别名 from 模块名import 功能名 as 别名 3.1.导入模块不推荐使用, 12import pkg1import pkg2 3.2.简单的使用my_module.py 1234567title = "模块2"def say_hello(): print("i am module : %s " % title)class Cat: pass index.py 1234567import my_module# use module methodmy_module.say_hello()dog = my_module.Cat()print(dog) 3.3.导入的时候也可以起别名别名要使用大驼峰命名 1import my_module as MyModule 3.4.from…import导入一部分工具 使用的时候,就不需要写那个模块名了,直接使用 1234from my_module import say_hellofrom my_module import Catsay_hello()cat = Cat() 3.5.作为模块的正常写法123456def main(): pass# 有了这个之后,被别的模块调用的时候if __name__ = "__main__" main 3.6.包包 包含多个模块 创建一个新的文件夹,在文件夹里面创建__init__.py 123# . 是相对路径名from . import send_messagefrom . import receive_message 在文件夹里面创建两个模块 receive_message.py 12def receive(): print("接受信息") send_message.py 12def send(text): print("发送 %s" % text) 调用模块 1234import hm_messagehm_message.send_message.send('hello')hm_message.receive_message.receive() 3.7.发布模块1.创建setup.py 1234567from distutils.core import setupsetup(name="hm_message", version="1.0", description="push", py_modules=["hm_message.send_message", "hm_message.receive_message"]) 2.命令 12python setup.py buildpython setup.py sdist 3.8.安装模块123# 手动安装模块tar xxx.tar.gzpython setup.py install pymssql访问https://www.lfd.uci.edu/~gohlke/pythonlibs/#pymssql 下载[pymssql‑2.1.4‑cp38‑cp38‑win32.whl] 网络编程0.socket的历史套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。 一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯或IPC。 套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 基于文件类型的套接字家族 - 套接字家族的名字:AF_UNIX unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信 基于网络类型的套接字家族 - 套接字家族的名字:AF_INET (还有AF_INET6被用于ipv6, 还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET) 套接字把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的 1.udp发送端 123456789101112131415161718192021import socketdef main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: send_data = str = input("输入要发送的数据") if send_data == "exit": break # udp_socket.sendto(b"这是消息",("192.169.0.1",8000)) udp_socket.sendto(send_data.encode("utf-8"),("127.0.0.1",7788)) # 关闭套接字 udp_socket.close()if __name__ == '__main__': main() 接受者 1234567891011121314151617181920import socketdef main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定本地相关信息 local_addr = ("",7788) udp_socket.bind(local_addr) while True: # 等待接收对方发送的数据 recv_data = udp_socket.recvfrom(1024) print(recv_data[0].decode("gbk")) # 关闭套接字 udp_socket.close()if __name__ == '__main__': main() 2.tcp3.socket使用socket访问redis 12345678910111213141516171819import sockethost = '10.0.2.110'port = 6379buf_size = 1conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)conn.connect((host, port))cmd = 'SELECT 2\n'.encode('utf-8')r = conn.sendall(cmd)cmd = 'PING\n'.encode('utf-8')conn.sendall(cmd)while True: res = conn.recv(buf_size) print(res) if not res: breakconn.close() 服务端套接字函数 123456789101112131415161718192021222324252627282930313233s.bind() #绑定(主机,端口号)到套接字s.listen() #开始TCP监听s.accept() #被动接受TCP客户的连接,(阻塞式)等待连接的到来**客户端套接字函数**s.connect() #主动初始化TCP服务器连接s.connect_ex() #connect()函数的扩展版本,出错时返回出错码,而不是抛出异常**公共用途的套接字函数(客户端和服务端都能使用)**s.recv() #接收TCP数据s.send() #发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)s.sendall() #发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)s.recvfrom() #接收UDP数据s.sendto() #发送UDP数据s.getpeername() #连接到当前套接字的远端的地址s.getsockname() #当前套接字的地址s.getsockopt() #返回指定套接字的参数s.setsockopt() #设置指定套接字的参数s.close() #关闭套接字 Requestpost请求参数123456789101112131415161718# 表单提交import requestsdata = { "name":"anthony", "age":"12"}requests.post(url=url,data=data)# json提交import requestsjson_data = { "name":"anthony", "age":"12"}requests.post(url=url,json=json_data) cookie操作1234cookies={ 'auther':'11223'}request.post(url=url,data=data,cookies=cookies) headers1234headers ={ 'auth':'123'}request.post(url=url,data=data,cookies=cookies,headers=headers) 请求超时12# 如果超过2s就超时报错requests.post(url=url,timeout=2) 鉴权有些页面,需要,比如spring secuity的页面 1requests.post(url=url,timeout=2,auth=('anthony','123456')) 编码12# 先编码 再解码r.text.encode('utf-8').decode(unicode_escape) 下载流21234567url = "http://wx4.sinaimg.cn/large/d030806aly1fq1vn8j0ajj21ho28bduy.jpg"rsp = requests.get(url, stream=True)with open('1.jpg', 'wb') as f: # 边下载边存硬盘, chunk_size 可以自由调整为可以更好地适合您的用例的数字 for i in rsp.iter_content(chunk_size=1024): f.write(i) requests.get(url)默认是下载在内存中的,下载完成才存到硬盘上 Response.iter_content来边下载边存硬盘 Flask路由入门12345678910from flask import Flaskapp = Flask(__name__)@app.route('/login')def hello_world(): return 'Hello World!'if __name__ == '__main__': app.run() URL上动态参数1234567891011121314from flask import Flaskapp = Flask(__name__)@app.route("/")def hello_world(): return "hello world"@app.route("/<int:age>")def play_game(age): return "hello world"+str(age)if __name__ == '__main__': app.run(debug=True) 自定义转换器1234567891011121314151617181920212223242526from flask import Flaskfrom werkzeug.routing import BaseConverterapp = Flask(__name__)# 自定义,并且继承BaseConverterclass MyConverter(BaseConverter): def __init__(self, map, regex): # map 就是app.url_map 也就是请求路径 # regex,就是d{3}, 也就是自定义的规则 super(MyConverter, self).__init__(map) self.regex = regexapp.url_map.converters["haha"] = [email protected]('/<haha("\d{3}"):age>')def play_game2(age): return "自定义转换器,接收3位" + str(age)@app.route('/<haha("\d{4}"):age>')def play_game3(age): return "自定义转换器,接收4位" + str(age)if __name__ == '__main__': app.run(debug=True) 给路由添加请求方式12345678910from flask import Flaskapp = Flask(__name__)@app.route('/',methods=['POST'])def play_game2(age): return "自定义转换器,接收3位" + str(age)if __name__ == '__main__': app.run(debug=True) 返回响应体123456789101112131415161718192021from flask import Flaskapp = Flask(__name__)@app.route('/')def method1(): """直接返回字符串信息""" return "返回字符串信息"@app.route('/2')def method2(): """直接返回字符串信息""" return "返回字符串信息",[email protected]('/3')def method3(): """直接返回字符串信息""" return {"name":"anthony"},200,{"Content-Type":"application/json","token":"123456"}if __name__ == '__main__': app.run(debug=True) 重定向12345678910from flask import Flask,redirectapp = Flask(__name__)@app.route('/')def method1(): return redirect("http://baidu.com")if __name__ == '__main__': app.run(debug=True) 跳转页面123456789101112131415161718192021222324from flask import Flask, render_template, request,redirectapp = Flask(__name__)@app.route('/login', methods=['GET', 'POST'])def hello_world(): print("请求来了") # 获取post传过来的值 user = request.form.get("user") pwd = request.form.get("pwd") if user == "anthony" and pwd == "123456": # return render_template("login.html", **{"msg": "登录成功"}) return redirect("/index") else: return render_template("login.html", **{"msg": "用户名或者密码错误"})@app.route("/index")def index(): return "欢迎登录"if __name__ == '__main__': app.run() 异常捕获123456789101112131415161718from flask import Flask,abortapp = Flask(__name__)@app.route('/')def method1(): """abort 异常抛出""" abort(404) return "hello world""""捕获异常"""@app.errorhandler(404)def method1(e): print(e) return "页面找不到"if __name__ == '__main__': app.run(debug=True) 获取请求参数Form12345678910111213141516171819from flask import Flask, render_template, requestapp = Flask(__name__)@app.route('/login', methods=['GET', 'POST'])def hello_world(): print("请求来了") # 获取post传过来的值 user = request.form.get("user") pwd = request.form.get("pwd") if user == "anthony" and pwd == "123456": return render_template("login.html", **{"msg": "登录成功"}) else: return render_template("login.html", **{"msg": "用户名或者密码错误"})if __name__ == '__main__': app.run() 获取请求参数Body123456789101112from flask import Flask, requestapp = Flask(__name__)@app.route('/callback')def hello_world(): data = request.get_data() print(data) return 'Hello, World!'if __name__ == "__main__": app.run(debug=True, port=10086) 环境变量/配置文件12345678910111213141516171819202122from flask import Flask,redirect,requestapp = Flask(__name__)# 1.从配置类中加载class MyConfig(object): DEBUG =True# app.config.from_object(MyConfig)# 2.从配置文件中加载# app.config.from_pyfile("Config.ini")# 在项目的根目录创建个Config.ini文件# 3.从环境变量# app.config.from_envvar("")@app.route('/')def method1(): passif __name__ == '__main__': app.run() 钩子,类似拦截器12345678910111213141516171819202122232425262728293031from flask import Flask,redirect,requestapp = Flask(__name__)@app.before_first_requestdef before_first_request(): """只请求一次""" print("before_first_request")@app.before_requestdef before_request(): """每次都会请求""" print("before_request")@app.after_requestdef after_request(resp): """比如做json统一的返回格式""" print("after_request") return [email protected]_requestdef teardown_request(e): """最后会请求到这里,适合做异常信息统计""" print("teardown_request")@app.route('/')def method1(): return "hello world"if __name__ == '__main__': app.run(debug=True) 视图内容和模板123456789101112131415161718192021from flask import Flask, make_response, requestapp = Flask(__name__)# 设置[email protected]("/set_cookie")def set_cookie(): response = make_response("set cookie") response.set_cookie("computer","macbook pro") response.set_cookie("age","13 pro",1000) return [email protected]("/get_cookie")def get_cookie(): name = request.cookies.get("computer") age = request.cookies.get("age") return "name:%s,age:%s"%(name,age)if __name__ == '__main__': app.run(debug=True) Session1234567891011121314151617181920from flask import Flask, make_response, request, sessionapp = Flask(__name__)app.config["SECRET_KEY"]="123456"# 设置[email protected]("/set_session/<path:name>")def set_session(name): session["name"] =name return "set session"@app.route("/get_session")def get_session(): value = session.get("name") return "value:%s"%(value)if __name__ == '__main__': app.run(debug=True) orm入门12pip install flask_sqlalchemypip install pymysql 1234567891011121314151617181920212223242526272829303132333435363738from flask import Flask,render_templatefrom flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)# 设置数据库的配置信息app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://Rong:[email protected]:3306/data36"app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False# 创建sqlAlchemy对象,关联appdb = SQLAlchemy(app)# 创建模型类class Role(db.Model): __tablename__ = "roles" id =db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32))class Users(db.Model): __tablename__ = "users" id =db.Column(db.Integer,primary_key=True) name = db.Column(db.String(32)) # 建立外键 role_id =db.Column(db.Integer,db.ForeignKey(Role.id))@app.route("/")def demo(): return "response"if __name__ == '__main__': # 创建数据库的表 db.drop_all() db.create_all() app.run(debug=True) 蓝图基本使用demo01.py 12345678910from flask import Flask, Blueprintfrom demo02 import blueapp = Flask(__name__)# 讲蓝图注册到app中app.register_blueprint(blue)if __name__ == '__main__': app.run(debug=True) demo02.py 123456789101112from flask import Blueprint# 创建蓝图对象blue = Blueprint("my_blue", __name__)@blue.route("/")def demo(): return "response"@blue.route("/2")def demo2(): return "response2" 蓝图包的使用目录结构:|—demo.py|—-|user||—-|user|–__init__.py|—-|user|–views.py demo.py 123456789101112from flask import Flaskfrom user import user_blueapp = Flask(__name__)# 讲蓝图注册到app中app.register_blueprint(user_blue)if __name__ == '__main__': print(app.url_map) app.run(debug=True) init.py 123456from flask import Blueprint# 创建蓝图对象user_blue = Blueprint("user", __name__)from user import views views.py 123456789from user import user_blue@user_blue.route("/")def demo(): return "response"@user_blue.route("/2")def demo2(): return "response2" Django命令12345678# 安装djangopip install django# 生成数据库 迁移文件python manage.py makemigrations# 执行迁移生成表python manage.py migrate PyCharm和Django-admin创建的项目不一样12345678910111213# 项目创建django-admin startproject 项目名# 结构项目名|----|manage.py [项目管理,启动项目,创建app,数据管理]|----|项目名同名文件夹|----|----|__init__.py|----|----|asgi.py [项目配置]|----|----|settings.py [路由]|----|----|urls.py [接收网路请求,异步]|----|----|wsgi.py [接收网路请求,同步] 12345678910# 用PyCharm创建的目录结构项目名|----|manage.py |----|templates文件夹|----|项目名同名文件夹|----|----|__init__.py|----|----|asgi.py|----|----|settings.py|----|----|urls.py|----|----|wsgi.py PyCharm创建的根目录下有一个templates目录Django-admin 是没有的,要在app下的目录创建templates settings.py 1234567891011121314151617181920212223242526272829303132333435# PyCharm创建的TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, },]# 命令行创建的TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, },] 创建应用123|项目||----|app,后台系统[独立的表结构,模板,不相互影响]|----|app,前台系统[独立的表结构,模板,不相互影响] 123456789101112131415161718192021# 创建apppython manage.py startapp app名字djangoProject├── app01 [刚创建的app]│ ├── __init__.py│ ├── admin.py [django默认admin后台,不用动]│ ├── apps.py [app启动类,不用动]│ ├── migrations [数据库的迁移的,数据库变更记录]│ │ └── __init__.py│ ├── models.py [重要,对数据库进行操作]│ ├── tests.py [单元测试]│ └── views.py [重要,函数]├── djangoProject│ ├── __init__.py│ ├── asgi.py│ ├── settings.py│ ├── urls.py│ └── wsgi.py├── manage.py└── templates 注册APP项目名→项目名同名文件夹→settings.py 12345678910INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # 添加下面一行的配置,下面这个是怎么来的呢?看下面 'app01.apps.App01Config' ] 项目名→APP文件夹→apps.py 12345from django.apps import AppConfigclass App01Config(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'app01' 编写URL和视图函数对应关系编辑urls.py 在app文件夹下的views.py 1234567from django.http import HttpResponsefrom django.shortcuts import render# Create your views here.# request 必须要的def index(request): return HttpResponse("欢迎使用") 启动12# 命令行启动python ./manage.py runserver templates模板 静态资源12345678910111213141516171819202122232425djangoProject├── app01│ ├── __init__.py│ ├── admin.py│ ├── apps.py│ ├── migrations│ │ └── __init__.py│ ├── models.py│ ├── static [手动创建的]│ │ ├── css [手动创建的] │ │ ├── img [手动创建的]│ │ │ └── 1.png│ │ └── js│ ├── tests.py│ └── views.py├── db.sqlite3├── djangoProject│ ├── __init__.py│ ├── asgi.py│ ├── settings.py│ ├── urls.py│ └── wsgi.py├── manage.py└── templates └── user_list.html 12345678910111213141516171819{% load static %}<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>用户列表</title></head><body><h1>用户列表</h1>加载静态资源写法一:<img src="/static/img/1.png">写法二:<img src="{% static 'img/1.png' %}"></body></html> 数据库主意:app需要先注册 12345python ./manage.py makemigrationspython ./manage.py migrate# Pycharm->tools->Run manager.py Task 开启,可以替代python manager.py 这个前缀 1pip3 install pymysql 12345678910111213141516171819from pymysql import connectcon = connect(host="43.240.30.52", port=3306, user="yanganli_github", password="yanganli_github", database="yanganli_github", charset="utf8")cursor = con.cursor()# 查询出条数print(cursor.execute("select * from post"))for one in cursor.fetchall(): print(one)cursor.close()con.close() 自动化测试安装和命令1234567891011121314# 安装pytestpip install pytest# 安装python-html包,生成html报告pip install pytest-html# 生成allure报告pip install allure-pytest# -s 打印printpytest -s test_auth.py# 只测试这一个文件pytest -q -s test_auth.py Pycharm识别pytest 规范测试文件已test_开头 测试类已Test开头,并且不能带有init方法 测试函数以test_开头 断言基本使用assert 生成报告123456789101112# 生成html'--html=../report/report.html'# 生成xml格式的报告'--junitxml=./report/report.xml'# 生成allure报告'--alluredir','../report/reportallure'# 生成allure报告,在report的文件夹执行pytest.main(['../test_case/','--alluredir','../report/allure_raw/'])./allure serve /myitem/autotest/report/allure_raw Locust1234567pip install locust# centos需要添加环境变量# 如果locust在哪个目录,如果python安装在这个目录:/usr/local/bin/python3ln -s /usr/local/bin/python3/bin/locust /usr/bin/locustlocust --version 快速入门新建一个redis.py 12345678910111213141516class QuickstartUser(HttpUser): wait_time = between(1, 10) @task def index_page(self): self.client.get("/hello") self.client.get("/world") @task(3) def view_item(self): for item_id in range(10): self.client.get(f"/item?id={item_id}", name="/item") time.sleep(1) def on_start(self): self.client.post("/login", json={"username": "foo", "password": "bar"}) 在文件目录,执行 123locust -f redis.py# 启动成功后,访问 http://localhost:8089 写一个Locust文件Locust文件是一个普通的python文件,唯一的要求就是至少有一个类继承的User类 User一个User类标识一个用户,Locust会给每个用户模拟一个User实例,公共的属性通常在User类里被定义 wait_time 属性用户的wwait_time方法通常作用于执行的任务简直等待多长时间 这里有三个内建的等待时间的函数 constant 一段固定的时间 between 最小和最大数之间的随机时间 constant_pacing 确保任务运行一次最多不超过x秒的适应时间 举个例子,让每个用户在每个任务执行之间等待0.5s到10s 12345678from locust import User, task, betweenclass MyUser(User): @task def my_task(self): print("executing my_task") wait_time = between(0.5, 10) 也可以直接在你的类里声明wait_time方法,举个例子,下面的User类,会睡眠1s,2s… 12345678class MyUser(User): last_wait_time = 0 def wait_time(self): self.last_wait_time += 1 return self.last_wait_time ... weight 属性如果文件中存在多个用户类,并且在命令行中没有指定用户类,则Locust将生成每个用户类的相同数量。您还可以通过将用户类作为命令行参数传递来指定从同一个locustfile中使用哪些用户类 1locust -f locust_file.py WebUser MobileUser 如果希望模拟某种类型的更多用户,可以在这些类上设置权重属性,比方说,web用户是移动用户的三倍多 1234567class WebUser(User): weight = 3 ...class MobileUser(User): weight = 1 ... host 属性host属性是host的URL前缀,通常,当locust启动的时候,在locust的web界面或者在命令上中使用--hosts选项中使用 如果一个类声明了声明的host属性,它将在没有使用--host命令行或web请求仲使用 tasks属性用户类可以使用@task装饰器,将任务声明为方法,但是也可以使用下面详细描述的tasks属性来指定任务。 environment 属性对用户正在其中运行的环境的引用,使用它影响这环境,或者是运行着在它的容器中,比如去停止运行从任务方法中 1self.environment.runner.quit() 如果运行的是独立的locust实例,它将停止全部,如果是运行在工作节点,它将停止节点 on_start 和 on_stop 方法用户 或者是任务集合可以声明``on_start方法或者on_stop方法,用户将调用它自己的on_start方法在它将要开始运行的时候,on_stop方法,将在停止运行的时候调用,比如TaskSet,on_start方法被调用在模拟的用户开始执行任务,on_stop方法在停止模拟的用户执行任务的时候调用(或者被interrupt()`方法调用,或者是用被用杀掉) Tasks当启动负载测试,一个用户实例将被创建, 下载mp4 12345678910111213141516171819202122232425262728293031323334353637383940414243import osimport timeimport requestsfrom tqdm import tqdm # 进度条模块def down_from_url(url, dst): # 设置stream=True参数读取大文件 response = requests.get(url, stream=True) # 通过header的content-length属性可以获取文件的总容量 file_size = int(response.headers['content-length']) if os.path.exists(dst): # 获取本地已经下载的部分文件的容量,方便继续下载,如果不存在就从头开始下载。 first_byte = os.path.getsize(dst) else: first_byte = 0 # 如果大于或者等于则表示已经下载完成,否则继续 if first_byte >= file_size: return file_size header = {"Range": f"bytes={first_byte}-{file_size}"} pbar = tqdm(total=file_size, initial=first_byte, unit='B', unit_scale=True, desc=dst) req = requests.get(url, headers=header, stream=True) with open(dst, 'ab') as f: # 每次读取一个1024个字节 for chunk in req.iter_content(chunk_size=1024): if chunk: f.write(chunk) pbar.update(1024) pbar.close() return file_sizeif __name__ == '__main__': url = input("请输入.mp4格式的视频链接地址,按回车键确认") # 根据时间戳生成文件名 down_from_url(url, str(time.time()) + ".mp4") SSH下载服务器文件 12345678910111213141516171819202122232425262728293031323334353637383940414243444546import paramikoclass LinuxFile: def __init__(self, ip, port, username, password): try: self.ip = ip self.port = port self.username = username self.password = password self.transport = paramiko.Transport((str(self.ip), int(self.port))) self.transport.connect(username=self.username, password=self.password) self.sftp = paramiko.SFTPClient.from_transport(self.transport) except Exception as e: raise e def up_file(self, localhost_file, server_file): """ 将本地文件上传至服务器 :param localhost_file: 本地文件路径 :param server_file: 服务器保存路径 :return: """ self.sftp.put(localhost_file, server_file) def down_file(self, localhost_file, server_file): """ 将服务器文件下载至本地 :param localhost_file: 本地文件路径 :param server_file: 服务器保存路径 :return: """ self.sftp.get(localhost_file, server_file) def close(self): """ 关闭服务器 :return: """ self.transport.close()if __name__ == '__main__': test = LinuxFile('47.242.218.75', '22', 'root', 'Qwer1234') # test.up_file('../2020-10-11_20-21-28.py', '/root/2020-10-11_20-21-28.py') test.down_file('/var/log/nginx/access.log','a.log')

2023-07-08
Telegram Bot
机器人设置消息分类消息分两种: 普通文本消息(比如test) 命令消息(以'/'开头的文本,比如 /test ) 默认在群里只能收到命令消息,和机器人私聊可以接收到全部的消息,如果想让机器人在群里也能接收到全部的消息,操作如下访问BotFather,输入/setprivacy,选择自己的机器人,选择DISABLED,就可以了

2022-11-19
Go
入门参考:Go英文文档 Hello World123456789// 一定要在main的包下才能运行// 一个main包下,只能有一个main入口package mainimport "fmt"func main() { fmt.Print("hello world")} 123456789# 第一种,编译后运行.exe文件# 编译C:\Users\anthony\go\src\awesomeProject\demo\main>go build demo.go# 运行编译后exe文件C:\Users\anthony\go\src\awesomeProject\demo\main>demo.exehello world# 第二种,直接运行源代码go run *.go 标准输入1234567891011121314151617package mainimport "fmt"func main() { var name string var age int /* 使用"&"获取score变量的内存地址(即取变量内存地址的运算符),通过键盘输入为score变量指向的内存地址赋初值。 fmt.Scan是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 fmt.Scan可以接收多个参数,用户输入参数默认使用空格或者回车换行符分割输入设备传入的参数,直到接收所有的参数为止 */ fmt.Scan(&name, &age) fmt.Println(name, age)} 1234567891011121314151617package mainimport "fmt"func main() { var name string var age int /* 和fmt.Scan功能类似,fmt.Scanln也是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 fmt.Scanln也可以接收多个参数,用户输入参数默认使用空格分割输入设备传入的参数,遇到回车换行符就结束接收参数 */ fmt.Scanln(&name, &age) fmt.Println(name, age)} 1234567891011121314151617181920212223package mainimport "fmt"func main() { var name string var age int /* 和fmt.Scanln功能类似,fmt.Scanf也是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 其实fmt.Scanln和fmt.Scanf可都以接收多个参数,用户输入参数默认使用空格分割输入设备传入的参数,遇到回车换行符就结束接收参数 唯一区别就是可以格式化用户输入的数据类型,如下所示: %s: 表示接收的参数会被转换成一个字符串类型,赋值给变量 %d: 表示接收的参数会被转换成一个整形类型,赋值给变量 生产环境中使用fmt.Scanln和fmt.Scanf的情况相对较少,一般使用fmt.Scan的情况较多~ */ fmt.Scanf("%s%d", &name, &age) fmt.Println(name, age)} 12345func method_if() { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n') fmt.Printf("输入的值是:%s\n", str)} 声明变量基本类型,声明变量,不赋值,就有默认值 1234567891011// 基本类型有默认值func main() { var age int = 0 fmt.Println(age) var code int fmt.Println(code) var sex bool fmt.Println(sex)} 一次性声明不同类型的变量 12345678910111213141516171819func main() { var age, name = 10, "anthony" println("可以声明不同类型,只能自动推导,不能定义类型:", age, name) // 报错,多变量赋值值,不能声明类型 // var age2 int,name2 string = 10, "anthony" // println(age2,name2)}func method6() { var ( age int = 23 name string = "anthony" sex bool ) println("使用集合的方式声明变量,可以声明不同类型,并且可以赋值", age, name, sex)} 先声明,后赋值 12345678func main() { // 定义变量 var i int // 给i赋值 i=10 // 使用变量 fmt.Print("i=", i)} 类型推导 1234func method2() { var name = "anthony" print("类型推导:", name)} 常量常量是一个简单值的标识符,在程序运行时,不会被修改的量 出于习惯,常量用大写,不过小写也没有问题 12345678910// 常量的格式定义const identifier [type] = value// 显式类型定义const A string = "abc"// 隐式类型定义const B = "abc"// 多个相同类型的声明可以简写为const C,D = 1,2// 多个不同类型的声明可以简写为const E, F, G = 1, false, "str" 常量用作枚举常量可以作为枚举,常量组 123456789101112131415161718func main() { const ( RED = 1 BLACK = 2 WHITE = 3 ) const ( x int = 16 y s = "abc" z ) fmt.Println(RED, BLACK, WHITE) fmt.Println("常量组中如不指定类型和初始化值,则与上一行非空常量右值相同:", x, y, s, z)} 特殊常量:iotaiota,特殊常量,可以认为是一个可以被编译器修改的常量 如果中断iota自增,则必须显式恢复。且后续自增值按行序递增 自增默认是int类型,可以自行进行显示指定类型 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址 1234567891011121314151617func demo() { // 报错 //a = iota const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a, b, c, d, e, f, g, h, i)} 字符串12345678func main() { name := "Hello World" for index := range name { fmt.Printf("遍历字符串:%v,%v\n",index,name[index]) // 字符串长度 fmt.Println(len(name[index])) }} 字符串切片12345var response = "var r = [[\"000001\",\"HXCZHH\",\"华夏成长混合\",\"混合型-偏股\",\"HUAXIACHENGZHANGHUNHE\"]];"fmt.Println(response[8:len(response)-1])// 打印[["000001","HXCZHH","华夏成长混合","混合型-偏股","HUAXIACHENGZHANGHUNHE"]] 字符串数组转一维数组1234567891011121314func main() { response := "[1,2,3]" var arr []int if err := json.Unmarshal([]byte(response), &arr); err != nil { fmt.Println("Error:", err) } // 第1行;数据是:1 // 第2行;数据是:2 // 第3行;数据是:3 for i:=0 ;i< len(arr);i++ { fmt.Printf("第%v行;数据是:%v\n",i+1,arr[i]) }} 字符串数组转二维数组12345678910111213func main() { response := "[[1,2,3],[2,3,4]]" var arr [][]int if err := json.Unmarshal([]byte(response), &arr); err != nil { fmt.Println("Error:", err) } // 第1行;数据是:[1 2 3] // 第2行;数据是:[2 3 4] for i:=0 ;i< len(arr);i++ { fmt.Printf("第%v行;数据是:%v\n",i+1,arr[i]) }} 字符串和数字互转1234567string转成int:int, err := strconv.Atoi(string)string转成int64:int64, err := strconv.ParseInt(string, 10, 64)int转成string:string := strconv.Itoa(int)int64转成string:string := strconv.FormatInt(int64,10) type关键字12345678910func main() { // 类型转换 var one int = 17 mean := float32(one) fmt.Println(mean) // 字符串不能强转整型} 流程控制123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120// iffunc method_if() { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n') if str == "" { fmt.Println("空的") } fmt.Printf("输入的值是:%s\n", str)}// if-ikfunc main() { var data = make(map[string]int) data["a"] = 0 data["b"] = 1 data["c"] = 2 // 遍历 for k, v := range data { fmt.Println(k, v) } if _, ok := data["a"]; ok { //存在 fmt.Println("存在") }}// if-elsefunc method_if2() { if num := 10; num%2 == 0 { //checks if number is even fmt.Println(num, "is even") } else { fmt.Println(num, "is odd") }}// switchfunc method_switch() { num := 1 result := -1 switch num { case 1: result = 1 case 2, 3, 4: result = 2 default: result = -2 } fmt.Println(result)}// for 开始012345678910结束func demo() { fmt.Print("开始") for i := 0; i <= 10; i++ { fmt.Print(i) } fmt.Print("结束")}// break 开始01234结束func demo_break() { fmt.Print("开始") for i := 0; i <= 10; i++ { if i == 5 { break } fmt.Print(i) } fmt.Print("结束")}// coutinue 1 3 5 7 9func demo_continue() { for i := 1; i <= 10; i++ { if i%2 == 0 { continue } fmt.Printf("%d ", i) }}// go-to// go// 会打印两次 ===func demo_goto() { /* 定义局部变量 */ var a = 0 /* 循环 */LOOP: fmt.Println("===") for a < 10 { if a == 5 { /* 跳过迭代 */ a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n", a) a++ }} 1 值传递和引用传递程序中使用的是值传递, 所以两个值并没有实现交互 123456789101112131415161718192021222324252627282930func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 fmt.Printf("交换前 a 的值为 : %d\n", a ) fmt.Printf("交换前 b 的值为 : %d\n", b ) /* 通过调用函数来交换值 */ swap(a, b) fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b )}/* 定义相互交换值的函数 */func swap(x, y int) int { var temp int temp = x /* 保存 x 的值 */ x = y /* 将 y 值赋给 x */ y = temp /* 将 temp 值赋给 y*/ return temp;}//交换前 a 的值为 : 100//交换前 b 的值为 : 200//交换后 a 的值 : 100//交换后 b 的值 : 200 引用传递 1234567891011121314151617181920212223242526272829func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 fmt.Printf("交换前,a 的值 : %d\n", a ) fmt.Printf("交换前,b 的值 : %d\n", b ) /* 调用 swap() 函数 * &a 指向 a 指针,a 变量的地址 * &b 指向 b 指针,b 变量的地址 */ swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n", a ) fmt.Printf("交换后,b 的值 : %d\n", b )}func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址上的值 */ *x = *y /* 将 y 值赋给 x */ *y = temp /* 将 temp 值赋给 y */}//交换前,a 的值 : 100//交换前,b 的值 : 200//交换后,a 的值 : 200//交换后,b 的值 : 100 变量作用域12345678910111213141516// 全局变量var global int = 32func main() { // 局部变量 var a,b int =1, 2 fmt.Printf("打印全局变变量:%v\n", global) global = a+ b fmt.Printf("打印全局变变量:%v\n", global) var global int = 3 fmt.Printf("打印全局变变量:%v\n", global)} 匿名代码块 匿名代码块被用来定义一个局部变量 message,该变量仅在代码块内部可见。一旦离开代码块,变量 message 就不再可见,任何尝试在代码块外部访问它都会导致编译错误。 1234567891011121314151617181920212223package mainimport ( "fmt")func main() { fmt.Println("在main函数外部") // 进入匿名代码块 { fmt.Println("在匿名代码块内部") // 定义一个局部变量,仅在代码块内可见 message := "这是一个局部变量" fmt.Println(message) } // message 变量在代码块外不可见 // fmt.Println(message) // 这会导致编译错误 fmt.Println("在main函数外部")} 数组和切片数组 12345678910111213141516171819202122232425262728293031323334353637func get() { list := [5]int{1,2,3,4,5} for i:=0;i< len(list);i++ { fmt.Println(i, "==", list[i]) } for k, v := range my { fmt.Println(k, v) }}func create(){ var numbers []int fmt.Println("新建个空数组:",numbers) var defaultcount [4]int fmt.Println("新建个指定长度的数组:",defaultcount) var balance = []int{1,2,3,4,5} fmt.Println("新建个不指定长度的数组:",balance) // 根据元素的个数,设置数组的大小 d := [...] int{1,2,3,4,5} fmt.Println("新建个指定位置的数组:",d) // 指定位置 e := [5] int{4: 100} // [0 0 0 0 100] fmt.Println("新建个指定位置的数组:",e) // 指定位置 f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1] fmt.Println("新建个指定位置的数组:",f)} 切片 12345678910111213141516171819202122232425262728293031323334func define() { var identifier []int fmt.Println("空数组", identifier) var slice1 []int = make([]int, 10) fmt.Println("切片", slice1) i0 := slice1[0] i1 := slice1[1] i2 := slice1[2] i3 := slice1[3] fmt.Println("通过索引,获取值", i0, i1, i2, i3) // 修改切片 for i := 0; i < len(slice1); i++ { slice1[i] = i } fmt.Println("修改完切片::", slice1) fmt.Println("获取切片区间1:", slice1[0:2]) fmt.Println("获取切片区间2:", slice1[5:]) fmt.Println("获取切片区间3:", slice1[:5]) slice1 = append(slice1, 10, 11, 12) fmt.Println("追加完切片::", slice1) slice2 := make([]int, len(slice1), cap(slice1)*2) fmt.Println("创建个容量是原来容量两位的数组:", slice2) number := copy(slice2, slice1) fmt.Printf("slice:%v,slice2:%v,number:%v:", slice1, slice2, number)} Map12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849func main() { keyvale := make(map[string]string) keyvale["k1"] = "v1" keyvale["k2"] = "v2" keyvale["k3"] = "v3" keyvale["k4"] = "v4" // 循环遍历 for key := range keyvale { fmt.Println("循环遍历:", key, keyvale[key]) } // 删除元素 delete(keyvale, "k1") for key := range keyvale { fmt.Println("删除值之后,循环遍历:", key, keyvale[key]) } // 查看元素是否存在 _, ok := keyvale["United States"] if ok { fmt.Printf("存在\n") } else { fmt.Printf("不存在\n") } // 但是当key如果不存在的时候,我们会得到该value值类型的默认值,比如string类型得到空字符串,int类型得到0。但是程序不会报错 m := make(map[string]int) m["a"] = 1 x, ok := m["b"] fmt.Println("key不存在时,会有默认值", x, ok) // map长度 fmt.Println("map长度", len(m)) // map是引用类型 mymap := map[string]int{ "steven": 12000, "anthony": 15000, } mymap["mike"] = 9000 fmt.Printf("原来的数据:%v\n", mymap) newmymap := mymap newmymap["anthony"] = 50000 fmt.Printf("改变后,原来的数据:%v\n", mymap) fmt.Printf("改变后,新的的数据:%v\n", newmymap)} 函数12345678910111213141516171819202122232425// 函数返回两个数的最大值func max(num1, num2 int) int { /* 声明局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result}// 函数调用func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 var ret int /* 调用函数并返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret )} 函数返回多个值12345678910// 入参多个值,x和y 都是字符串// 返回值是多个,都是字符串func swap(x, y string) (string, string) { return y, x}func main() { a, b := swap("Google", "Runoob") fmt.Println(a, b)} 函数返回值的命名123456789101112131415161718type studen struct { name string age int}func test4() { stu := test4_return() fmt.Println(stu.name)}func test4_return() (stu *studen) { stu = &studen{ "anthony", 13, } return} 函数作为实参12345678910111213141516171819type fb func(x int) intfunc main() { myfunc := func(x int) int{ return x } // 知识点1 fmt.Println(myfunc(3)) // 知识点2 demo(2, myfunc)}// 函数作为参数传递,实现回调func demo(x int,myfb fb) { myfb(x)} 函数作为实参-回调1234567891011121314151617181920func main() { test2()}func test2() { group("/system1", myfunc) group("/system2", func(path2 string) { fmt.Printf("回调Path:%v \n", path2) })}func group(path string, group func(path2 string)) { fmt.Printf("手动实现 \n" + path) group(path)}func myfunc(path2 string) { fmt.Printf("回调Path:%v \n", path2)} 函数作为参数-interface{}12345678func test3() { test3_object(myfunc)}func test3_object(object interface{}) { a := object fmt.Printf("什么鬼:%v", a)} 指针 12345678910111213141516171819202122232425262728293031func main() { var a int= 20 /* 声明实际变量 */ var ip *int /* 声明指针变量 */ ip = &a /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %x\n", &a ) /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %d\n", *ip )}// 函数使用指针func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b )}/* 交换函数这样写更加简洁,也是 go 语言的特性,可以用下,c++ 和 c# 是不能这么干的 */func swap(x *int, y *int){ *x, *y = *y, *x} 结构体123456789101112131415func main() { // 返回的是该实例的结构类型 var s people s.age = 1 fmt.Println(s) // 第二第三种,返回的是一个指向这个结构类型的一个指针,是地址 ming := new(people) ming.name = "xiao ming" ming.age = 18 fmt.Println(ming) ming2 := &people{"xiao ming", 18} fmt.Println(ming2)} 第二第三种返回指针的声明形式,在我们需要修改他的值的时候,其实应该使用的方式是: 1(*ming).name = "xiao wang" 也就是说,对于指针类型的数值,应该要先用*取值,然后再修改。 但是,在Golang中,可以省略这一步骤,直接使用ming.name = "xiao wang"。 方法1234567891011121314151617181920212223242526type Vertex struct { X, Y float64}func (v Vertex) test1() { v.X++ v.Y++}func (v *Vertex) test2() { v.X++ v.Y++}func main() { v1 := Vertex{1, 1} v2 := &Vertex{1, 1} v1.test1() v2.test2() // {1 1} fmt.Println(v1) // &{2 2} fmt.Println(v2)} test1和test2,他们唯一的区别就是方法名前面的接收者不同,一个是指针类型的,一个是值类型的。 测试1234567891011121314151617type Vertex struct { X, Y float64}func (v Vertex) test1(){ // 自己定义的v1内存地址为:0xc00000a0e0 fmt.Printf("在方法中的v的地址为:%p\n", &v) v.X++; v.Y++;}func main() { v1 := Vertex{1, 1} // 在方法中的v的地址为:0xc00000a100 fmt.Printf("自己定义的v1内存地址为:%p\n", &v1) v1.test1()} 1234567891011121314151617type Vertex struct { X, Y float64}func (v *Vertex) test2(){ // 自己定义的v1内存地址为:0xc00000a0e0 fmt.Printf("在方法中的v的地址为:%p\n", v) v.X++; v.Y++;}func main() { v1 := &Vertex{1, 1} // fmt.Printf("自己定义的v1内存地址为:%p\n", v1) v1.test2()} 123456789101112131415161718192021222324252627282930313233type Books struct { title string author string subject string book_id int}type Library struct { // 匿名字段,那么默认Student就包含了Human的所有字段 Books address string}func main() { var book Books book.title = "Go 语言" book.author = "www.runoob.com" book.subject = "Go 语言教程" book.book_id = 6495407 // 初始化一个图书馆 mark := Library{Books{"Go 语言","www.runoob.com","Go 语言教程",6495407},"广东"} // 我们访问相应的字段 fmt.Println("His name is ", mark.title) fmt.Println("His age is ", mark.author) fmt.Println("His weight is ", mark.subject) fmt.Println("His speciality is ", mark.address) // 修改对应的备注信息 mark.title = "AI" fmt.Println("Mark changed his speciality") fmt.Println("His speciality is ", mark.title)} 面向对象123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778// 定义接口type PersonMethod interface { show()}// 定义结构体type Person struct { name string age int}// 对象成员方法func (person *Person) setAge(age int) { person.age = age}// 对象成员方法实现接口func (person Person) show() { fmt.Printf("Person类打印SHOW:%v\n", person)}// 多态func show2(person PersonMethod) { person.show()}// 学生类,继承Person类type Student struct { // 匿名,相当于继承 Person level string}func (student *Student) show() { fmt.Printf("Student类打印SHOW:%v\n", student)}// 老师类,继承Person类type Teacher struct { Person price int}func (teacher Teacher) show() { fmt.Printf("Teacher类打印SHOW:%v\n", teacher)}func main() { anthony := Person{"anthony", 25} fmt.Printf("anthony信息:%v\n", anthony) // 调用成员方法 anthony.setAge(12) fmt.Printf("anthony信息:%v\n", anthony) anthony2 := Person{} fmt.Printf("anthony2信息:%v\n", anthony2) anthony2.age = 26 anthony2.name = "anthony2" fmt.Printf("anthony2信息:%v\n", anthony2) // 学生,继承 student := Student{} student.level = "小学生" student.name = "anthony" student.age = 23 fmt.Printf("学生继承类:%v\n", student) // 老师,继承 teacher := Teacher{price: 12, Person: Person{name: "li teacher", age: 56}} fmt.Printf("老师继承类:%v\n", teacher) show2(&student) show2(teacher) show2(anthony)} 创建对象的方式 12345678910111213141516171819// 方式一:使用T{…}方式,结果为值类型c := Car{}// 方式二:使用new的方式,结果为指针类型c1 := new(Car)// 方式三:使用&方式,结果为指针类型c2 := &Car{}// 以下为创建并初始化c3 := &Car{"红色", "1.2L"}c4 := &Car{color: "红色"}c5 := Car{color: "红色"}// 没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX 来命名,表示“构造函数” :func NewCar(color,size string)*Car { return &Car{color,size}} 接口12345678910111213141516171819202122232425262728type Phone interface { call()}type NokiaPhone struct {}func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!")}type IPhone struct {}func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!")}func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call()} 错误处理123456789101112131415161718192021222324252627282930313233343536373839404142434445// 自定义错误信息结构type DivErr struct { etype int // 错误类型 v1 int // 记录下出错时的除数、被除数 v2 int}// 实现接口方法 error.Error()func (divErr DivErr) Error() string { if 0== divErr.etype { return "除零错误" }else{ return "其他未知错误" }}// 除法func div(a int, b int) (int,*DivErr) { if b == 0 { // 返回错误信息 return 0,&DivErr{0,a,b} } else { // 返回正确的商 return a / b, nil }}func main() { // 正确调用 v,r :=div(100,2) if nil!=r{ fmt.Println("(1)fail:",r) }else{ fmt.Println("(1)succeed:",v) } // 错误调用 v,r =div(100,0) if nil!=r{ fmt.Println("(2)fail:",r) }else{ fmt.Println("(2)succeed:",v) }} GoRoutine1234567891011func main() { go say("world") say("hello")}func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }} Channel12345678910111213package mainimport "fmt"func main() { messages := make(chan string) go func() { messages <- "ping" }() msg := <-messages fmt.Println(msg)} 包管理器modulesgo mod常用命令 命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖) 示例一:创建一个新项目 在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件 go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。 go.mod 提供了module, require、replace和exclude 四个命令 module 语句指定包的名字(路径) require 语句指定的依赖项模块 replace 语句可以替换依赖项模块 exclude 语句可以忽略依赖项模块 添加依赖 新建一个 server.go 文件,写入以下代码: 123456789101112131415package mainimport ( "net/http" "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323"))} 执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载: 1234567891011121314151617181920$ go run server.gogo: finding github.com/labstack/echo v3.3.10+incompatiblego: downloading github.com/labstack/echo v3.3.10+incompatiblego: extracting github.com/labstack/echo v3.3.10+incompatiblego: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding github.com/labstack/gommon v0.2.8# 此处省略很多行... ____ __ / __/___/ / ___ / _// __/ _ \/ _ \/___/\__/_//_/\___/ v3.3.10-devHigh performance, minimalist Go web frameworkhttps://echo.labstack.com____________________________________O/_______ O\⇨ http server started on [::]:1323复制代码 现在查看go.mod 内容: 123456789101112131415$ cat go.modmodule hellogo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect)复制代码 go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍。 go 会自动生成一个 go.sum 文件来记录 dependency tree: 123456789$ cat go.sumgithub.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=... 省略很多行复制代码 再次执行脚本 go run server.go 发现跳过了检查并安装依赖的步骤。 可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u 升级所有依赖 go get 升级 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) 运行 go get -u=patch 将会升级到最新的修订版本 运行 go get package@version 将会升级到指定的版本号version 运行go get如果有版本的更改,那么go.mod文件也会更改 示例二:改造现有项目(helloword)项目目录为: 1234567$ tree.├── api│ └── apis.go└── server.go1 directory, 2 files server.go 源码为: 12345678910111213package mainimport ( api "./api" // 这里使用的是相对路径 "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}复制代码 api/apis.go 源码为: 123456789101112package apiimport ( "net/http" "github.com/labstack/echo")func HelloWorld(c echo.Context) error { return c.JSON(http.StatusOK, "hello world")}复制代码 使用 go mod init *** 初始化go.mod 123$ go mod init helloworldgo: creating new go.mod: module helloworld复制代码 运行 go run server.go 123456go: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding golang.org/x/crypto/acme/autocert latestgo: finding golang.org/x/crypto/acme latestgo: finding golang.org/x/crypto latestbuild command-line-arguments: cannot find module for path _/home/gs/helloworld/api 首先还是会查找并下载安装依赖,然后运行脚本 server.go,这里会抛出一个错误: 1build command-line-arguments: cannot find module for path _/home/gs/helloworld/api 但是go.mod 已经更新: 12345678910111213$ cat go.modmodule helloworldgo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect) 那为什么会抛出这个错误呢?这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且变更引入方法,必须将 helloworld当成路径的前缀,也就是需要写成 import helloworld/api,以往 GOPATH/dep 模式允许的 import ./api 已经失效,详情可以查看这个 issue。 更新旧的package import 方式 所以server.go 需要改写成: 12345678910111213package mainimport ( api "helloworld/api" // 这是更新后的引入方法 "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}复制代码 一个小坑:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod 🤷♂️ 网络编程最简单的网络编程123456789101112131415161718package mainimport ( "io" "log" "net/http")func HelloServer(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world!\n")}func main() { http.HandleFunc("/hello", HelloServer) err := http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }} TLS服务端和客户端serve.go 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package mainimport ( "bufio" "crypto/tls" "log" "net")func main() { log.SetFlags(log.Lshortfile) key, err := tls.LoadX509KeyPair("cert.pem", "key.pem") if err != nil { log.Println(err) return } config := &tls.Config{Certificates: []tls.Certificate{key}} ln, err := tls.Listen("tcp", ":8000", config) if err != nil { log.Println(err) return } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Println(err) continue } go handleConnection(conn) }}func handleConnection(conn net.Conn) { defer conn.Close() r := bufio.NewReader(conn) for { msg, err := r.ReadString('\n') if err != nil { log.Println(err) return } println(msg) n, err := conn.Write([]byte("world\n")) if err != nil { log.Println(n, err) return } }} clinet.go 123456789101112131415161718192021222324252627282930package mainimport ( "crypto/tls" "log")func main() { log.SetFlags(log.Lshortfile) conf := &tls.Config{ InsecureSkipVerify: true, } conn, err := tls.Dial("tcp", "127.0.0.1:8000", conf) if err != nil { log.Println(err) return } defer conn.Close() n, err := conn.Write([]byte("hello\n")) if err != nil { log.Println(n, err) return } buf := make([]byte, 100) n, err = conn.Read(buf) if err != nil { log.Println(n, err) return } println(string(buf[:n]))} 标准库flag1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768import ( "errors" "flag" "fmt" "strings" "time")/**// 编译go build demo.go// 帮助信息demo.exe -h// 正常使用demo.exe -name yang*/func main() { //demo1() demo2()}// 第一种用法func demo1() { var nFlag = flag.String("name", "anthony", "help message for flag n") flag.Parse() fmt.Println(*nFlag)}// 第二种用法func demo2() { var name string flag.StringVar(&name, "name", "anthonyyang", "help message for flag n") flag.Parse() fmt.Println(name)}// 第三种用法func demo3() { var intervalFlag mystring flag.Var(&intervalFlag, "anthonyyang", "name") fmt.Println(intervalFlag)}type mystring []time.Durationfunc (i *mystring) String() string { return fmt.Sprint()}func (i *mystring) Set(value string) error { if len(*i) > 0 { return errors.New("interval flag already set") } for _, dt := range strings.Split(value, ",") { duration, err := time.ParseDuration(dt) if err != nil { return err } *i = append(*i, duration) } return nil} 参考 logioio.Reader对于要用作读取器的类型,它必须实现 io.Reader 接口的唯一一个方法 Read(p []byte)。换句话说,只要实现了 Read(p []byte) ,那它就是一个读取器。 123type Reader interface { Read(p []byte) (n int, err error)} Read() 方法有两个返回值,一个是读取到的字节数,一个是发生错误时的错误。同时,如果资源内容已全部读取完毕,应该返回 io.EOF 错误。 io.Copysyncsync.WaitGroup1234567891011121314import ( "fmt")// 执行代码很可能看不到输出,因为有可能这两个协程还没得到执行主协程已经结束了,而主协程结束时会结束所有其他协程func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }()} 解决办法: 1234567891011121314151617import ( "fmt" "time")// 在main函数结尾加上等待func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }() time.Sleep(time.Second * 1) // 睡眠1秒,等待上面两个协程结束} 问题来了,不确定协程到底要用运行多长时间,比如协程需要用到2s,这里main等待1s,就不够用了 解决办法:用管道 12345678910111213141516171819202122232425262728import ( "fmt")func main() { ch := make(chan struct{}) count := 2 // count 表示活动的协程个数 go func() { fmt.Println("Goroutine 1") ch <- struct{}{} // 协程结束,发出信号 }() go func() { fmt.Println("Goroutine 2") ch <- struct{}{} // 协程结束,发出信号 }() for range ch { // 每次从ch中接收数据,表明一个活动的协程结束 count-- // 当所有活动的协程都结束时,关闭管道 if count == 0 { close(ch) } }} 虽然比较完美的方案,但是Go提供了更简单的方法——使用sync.WaitGroup WaitGroup顾名思义,就是用来等待一组操作完成的。 WaitGroup内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法,Add()用来添加计数。 Done()用来在操作结束时调用,使计数减一。Wait()用来等待所有的操作结束,即计数变为0,该函数会在计数不为0时等待,在计数为0时立即返回 12345678910111213141516171819202122import ( "fmt" "sync")func main() { var wg sync.WaitGroup wg.Add(2) // 因为有两个动作,所以增加2个计数 go func() { fmt.Println("Goroutine 1") wg.Done() // 操作完成,减少一个计数 }() go func() { fmt.Println("Goroutine 2") wg.Done() // 操作完成,减少一个计数 }() wg.Wait() // 等待,直到计数为0} nethttpGet不带参数 123456789101112131415161718func main() { resp, err := http.Get("http://fund.eastmoney.com/js/fundcode_search.js") if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) // 打印body fmt.Println(string(body)) // 打印状态码 fmt.Println(resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("ok") parse(string(body)) }} net.ListenListen通知本地网络地址。 网络必须是“tcp”,“tcp4”,“tcp6”,“unix”或“unixpacket”。 对于TCP网络,如果地址参数中的主机为空或文字未指定的IP地址,则Listen会监听本地系统的所有可用单播和任播IP地址。 要仅使用IPv4,请使用网络“tcp4”。 该地址可以使用主机名称,但不建议这样做,因为它将为主机的至多一个IP地址创建一个监听器。 如果地址参数中的端口为空或“0”,如“127.0.0.1:”或“:: 1:0”中所示,则会自动选择一个端口号。Listener的Addr方法可用于发现所选端口。 12345678910for retry := 0; retry < 16; retry++ { l, err := net.Listen("tcp", host+":0") if err != nil { continue } defer l.Close() _, port, err := net.SplitHostPort(l.Addr().String()) p, err := strconv.ParseInt(port, 10, 32) return int(p)} net.Conn.SetReadDeadline设置超时时间 net.Conn为Deadline提供了多个方法Set[Read|Write]Deadline(time.Time)。 Deadline是一个绝对时间值,当到达这个时间的时候,所有的 I/O 操作都会失败,返回超时(timeout)错误。 OSStat12345678910111213141516171819202122232425262728package mainimport ( "fmt" "os")func main() { fileinfo, err := os.Stat(`C:\Users\Administrator\Desktop\UninstallTool.zip`) if err != nil { panic(err) } fmt.Println(fileinfo.Name()) //获取文件名 fmt.Println(fileinfo.IsDir()) //判断是否是目录,返回bool类型 fmt.Println(fileinfo.ModTime()) //获取文件修改时间 fmt.Println(fileinfo.Mode()) fmt.Println(fileinfo.Size()) //获取文件大小 fmt.Println(fileinfo.Sys())}type FileInfo interface { Name() string // 文件的名字(不含扩展名) Size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同 Mode() FileMode // 文件的模式位 ModTime() time.Time // 文件的修改时间 IsDir() bool // 等价于Mode().IsDir() Sys() interface{} // 底层数据来源(可以返回nil)} IsExist返回一个布尔值说明该错误是否表示一个文件或目录已经存在。ErrExist和一些系统调用错误会使它返回真。 12345678910func check() bool { stat, err := os.Stat("/Users/anthony/Desktop/go-testt/go.mod") if err != nil { if os.IsExist(err) { return true } return false } return true} Text/templatehtml模板 https://pkg.go.dev/text/template GinDemo1234567891011package mainimport "github.com/gin-gonic/gin"func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(200, "Hello, Geektutu") }) r.Run() // listen and serve on 0.0.0.0:8080} 路由基本使用 1234567891011121314151617181920// 设置路由router := gin.Default()// 第一个参数是:路径; 第二个参数是:具体操作 func(c *gin.Context)router.GET("/Get", getting)router.POST("/Post", posting)router.PUT("/Put", putting)router.DELETE("/Delete", deleting)// 默认启动的是 8080端口router.Run()// 第一种方法:r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "Who are you?")})// 第二种方法r.POST("createApi", CreateApi)func CreateApi(c *gin.Context) { // 代码} 路由分组 12345678910111213// 两个路由组,都可以访问,大括号是为了保证规范v1 := r.Group("/v1"){ // 通过 localhost:8080/v1/hello访问,以此类推 v1.GET("/hello", sayHello) v1.GET("/world", sayWorld)}v2 := r.Group("/v2"){ v2.GET("/hello", sayHello) v2.GET("/world", sayWorld)}r.Run(":8080") 大量路由实现 建立routers包,将不同模块拆分到多个go文件 每个文件提供一个方法,该方法注册实现所有的路由 之后main方法在调用文件的方法实现注册 第一种实现方式 1234567891011121314151617// 这里是routers包下某一个router对外开放的方法func LoadRouter(e *gin.Engine) { e.Group("v1") { v1.GET("/post", postHandler) v1.GET("/get", getHandler) } ...}func main() { r := gin.Default() // 调用该方法实现注册 routers.LoadRouter(r) routers.LoadRouterXXX(r) // 代表还有多个 r.Run()} 第二种方式 1像vue一样注册路由,没看懂项目 为啥要注册很多次 获取参数获取body的数据,并绑定实体类 12345678910func (b *BaseApi) Login(c *gin.Context) { // 声明实体类 var l systemReq.Login // 赋值 err := c.ShouldBindJSON(&l) // 获取ip key := c.ClientIP()} 响应处理1234567891011121314151617181920212223// 1.JSONr.GET("/someJSON", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Json", "status": 200, })})// 2.XMLr.GET("/someXML", func(c *gin.Context) { c.XML(200, gin.H{"message": "abc"})})// 3.YAMLr.GET("/someYAML", func(c *gin.Context) { c.YAML(200, gin.H{"name": "zhangsan"})})// 4.protobufr.GET("/someProtoBuf", func(c *gin.Context) { reps := []int64{1, 2} data := &protoexample.Test{ Reps: reps, } c.ProtoBuf(200, data)}) 中间件中间件分为:全局中间件 和 路由中间件,区别在于前者会作用于所有路由。 1234567891011121314// 默认的中间件有func BasicAuth(accounts Accounts) HandlerFunc // 身份认证func BasicAuthForRealm(accounts Accounts, realm string) HandlerFuncfunc Bind(val interface{}) HandlerFunc //拦截请求参数并进行绑定func ErrorLogger() HandlerFunc //错误日志处理func ErrorLoggerT(typ ErrorType) HandlerFunc //自定义类型的错误日志处理func Logger() HandlerFunc //日志记录func LoggerWithConfig(conf LoggerConfig) HandlerFuncfunc LoggerWithFormatter(f LogFormatter) HandlerFuncfunc LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFuncfunc Recovery() HandlerFuncfunc RecoveryWithWriter(out io.Writer) HandlerFuncfunc WrapF(f http.HandlerFunc) HandlerFunc //将http.HandlerFunc包装成中间件func WrapH(h http.Handler) HandlerFunc //将http.Handler包装成中间件 作用范围 1234567891011121314// 作用于全局r.Use(gin.Logger())r.Use(gin.Recovery())// 作用于单个路由r.GET("/benchmark", MyBenchLogger(), benchEndpoint)// 作用于某个组authorized := r.Group("/")authorized.Use(AuthRequired()){ authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint)} 自定义中间件 123456789101112func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // 给Context实例设置一个值 c.Set("geektutu", "1111") // 请求前 c.Next() // 请求后 latency := time.Since(t) log.Print(latency) }} 控制中间件 中间件控制的方法gin提供了两个函数Abort()和Next(),二者区别在于: next()函数会跳过当前中间件中next()后的逻辑,当下一个中间件执行完成后再执行剩余的逻辑 abort()函数执行终止当前中间件以后的中间件执行,但是会执行当前中间件的后续逻辑 举例子更好理解: 我们注册中间件顺序为m1、m2、m3,如果采用next(): 执行顺序就是 m1的next()前面、m2的next()前面、m3的next()前面、 业务逻辑 m3的next()后续、m2的next()后续、m1的next()后续。 那如果m2中间调用了Abort(),则m3和业务逻辑不会执行,只会执行m2的next()后续、m1的next()后续。 GormCRUD 接口创建创建记录12345678// 没有数据的字段,就是基本数据user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result := db.Create(&user) // 通过数据的指针来创建user.ID // 返回插入数据的主键result.Error // 返回 errorresult.RowsAffected // 返回插入记录的条数 用指定的字段创建记录创建记录并更新给出的字段。 123// 没有数据的字段,插入的是nulldb.Select("Name", "Age", "CreatedAt").Create(&user)// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775") 创建一个记录且一同忽略传递给略去的字段值 12db.Omit("Name", "Age", "CreatedAt").Create(&user)// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775") ❓ 用指定的字段创建记录 不返回ID 批量插入1234567891011var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}db.Create(&users)for _, user := range users { user.ID // 1,2,3}// 使用 CreateInBatches 分批创建时,你可以指定每批的数量var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}// 数量为 100db.CreateInBatches(users, 100) 创建钩子123456789101112131415161718192021222324252627282930313233343536373839404142434445464748type SysMenu struct { gorm.Model // 菜单名称 Title string // 直属上级菜单标识 ParentId int // 菜单URL Path string // 0:Flase,1:True AlwaysShow int // 组件 Component string // 0:Flase,1:True Hidden int // 图标 Icon string // 0:Flase,1:True NoCache string // 前端内部名字 Name string // 前端内部路由 Redirect string // 0:否,1:是 IsButton int // 请求后端接口 Ajax string}// 钩子func (s *SysMenu) BeforeSave(tx *gorm.DB) (err error) { fmt.Println("BeforeSave") return}func (s *SysMenu) BeforeCreate(tx *gorm.DB) (err error) { fmt.Println("BeforeCreate") return}func (s *SysMenu) AfterCreate(tx *gorm.DB) (err error) { fmt.Println("AfterCreate") return}func (s *SysMenu) AfterSave(tx *gorm.DB) (err error) { fmt.Println("AfterSave") return} 根据 Map 创建123456789db.Model(&User{}).Create(map[string]interface{}{ "Name": "jinzhu", "Age": 18,})// batch insert from `[]map[string]interface{}{}`db.Model(&User{}).Create([]map[string]interface{}{ {"Name": "jinzhu_1", "Age": 18}, {"Name": "jinzhu_2", "Age": 20},}) 默认值12345type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"`} Cobra简介cobra是一个命令行程序库,可以用来编写命令行程序。 快速入门安装包 1$ go get github.com/spf13/cobra/cobra 文件目录 12345- my-go-test - cmd - version - root main.go main.go 12345import "go-test/cmd"func main() { cmd.Execute()} version.go 1234567891011121314151617181920package cmdimport ( "fmt" "github.com/spf13/cobra")var versionCmd = &cobra.Command{ Use: "version", Short: "显示计算器版本", Long: "显示计算器版本", Run: func(cmd *cobra.Command, args []string) { fmt.Println("anthony's jsq version 0.0.1") },}func init() { rootCmd.AddCommand(versionCmd)} root.go 1234567891011121314151617import ( "fmt" "github.com/spf13/cobra")var rootCmd = &cobra.Command{ Use: "jsq", Short: "计算器", Long: `anthony写的计算器`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("anthony写的计算器,请使用 ./jsq -h") },}func Execute() { rootCmd.Execute()} 打包运行123456789101112131415161718192021222324252627282930313233# 打包➜ go-testt go build -o jsq# 运行不带参数的命令➜ go-testt ./jsq anthony写的计算器,请使用 ./jsq -h# 运行主命令的help➜ go-testt ./jsq -helpError: unknown shorthand flag: 'e' in -elpUsage: jsq [flags] jsq [command]Available Commands: completion Generate the autocompletion script for the specified shell help Help about any command version 显示计算器版本Flags: -h, --help help for jsqUse "jsq [command] --help" for more information about a command.# 运行子命令➜ go-testt ./jsq version -h显示计算器版本Usage: jsq version [flags]Flags: -h, --help help for version 包成window包 1go build -o jsq.exe 库Yaml解析库1go get gopkg.in/yaml.v3 测试文件yaml 1234567mysql: url: 127.0.0.1 port: 3306redis: host: 127.0.0.1 port: 6379 将 yaml 文件的数据转成自定义的结构体或 Map 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import ( "fmt" "gopkg.in/yaml.v3" "os")type Config struct { Mysql Mysql `json:"mysql"` Redis Redis `json:"redis"`}type Mysql struct { Url string Port int}type Redis struct { Host string Port int}func main() { dataBytes, err := os.ReadFile("test.yaml") if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Println("yaml 文件的内容: \n", string(dataBytes)) config := Config{} err = yaml.Unmarshal(dataBytes, &config) if err != nil { fmt.Println("解析 yaml 文件失败:", err) return } fmt.Printf("config → %+v\n", config) // config → {Mysql:{Url:127.0.0.1 Port:3306} Redis:{Host:127.0.0.1 Port:6379}} mp := make(map[string]any, 2) err = yaml.Unmarshal(dataBytes, mp) if err != nil { fmt.Println("解析 yaml 文件失败:", err) return } fmt.Printf("map → %+v", config) // config → {Mysql:{Url:127.0.0.1 Port:3306} Redis:{Host:127.0.0.1 Port:6379}}} yaml解析库1go get github.com/spf13/viper 测试 123456789101112131415161718192021import ( "fmt" "github.com/spf13/viper")func main() { // 设置配置文件的名字 viper.SetConfigName("test") // 设置配置文件的类型 viper.SetConfigType("yaml") // 添加配置文件的路径,指定 config 目录下寻找 viper.AddConfigPath("./config") // 寻找配置文件并读取 err := viper.ReadInConfig() if err != nil { panic(fmt.Errorf("fatal error config file: %w", err)) } fmt.Println(viper.Get("mysql")) // map[port:3306 url:127.0.0.1] fmt.Println(viper.Get("mysql.url")) // 127.0.0.1} 笔记字符串互转byte1234// string to []bytes1 := "hello" b := []byte(s1)// []byte to strings2 := string(b) 创建对象的方式 12345678910111213141516171819202122232425type Car struct { color string size string}// 方式一:使用T{…}方式,结果为值类型c := Car{}// 方式二:使用new的方式,结果为指针类型c1 := new(Car)// 方式三:使用&方式,结果为指针类型c2 := &Car{}// 以下为创建并初始化c3 := &Car{"红色", "1.2L"}c4 := &Car{color: "红色"}c5 := Car{color: "红色"}// 构造函数:// 在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以// NewXXX 来命名,表示“构造函数” :func NewCar(color,size string)*Car { return &Car{color,size}} 报错1.debug call has arguments but no formatting directives 打印/日志 格式解析错误,错误的原因就是没加%v 2.取消代理 123456go envGOPROXY='https://goproxy.cn,direct' # 这个是国内的代理,在国外不容易下载依赖➜ server git:(main) ✗ go env -u GOPROXY # 取消代理,使用默认的配置➜ server git:(main) ✗ go envGOPROXY='https://proxy.golang.org,direct'
评论