十七、网上商城项目(5)

本章概要

  • 购物车
    • 购物车状态管理配置
    • 购物车组件
  • 结算页面
  • 用户管理
    • 用户状态管理配置
    • 用户注册组件
    • 用户登录组件

17.8 购物车

在一个电商网站中,购物车在很多页面都需要用到,因此非常适合放在 Vuex 的 store 中进行集中管理。在本项目中,采用模块化的方式管理应用中不同的状态。

17.8.1 购物车状态管理配置

在项目的 store 目录下新建 modules 文件夹,在该文件下新建 cart.js。如下:

store/modules/cart.js

const state = {
    items: []
}
//mutations
const mutations = {
    //添加商品到购物车中
    pushProductToCart(state, { id, imgUrl, title, price, quantity }) {
        if (!quantity)
            quantity = 1;
        state.items.push({ id, imgUrl, title, price, quantity });
    },

    //增加商品数量
    incrementItemQuantity(state, { id, quantity }) {
        let cartItem = state.items.find(item => item.id == id);
        cartItem.quantity += quantity;
    },
    //用于清空购物车
    setCartItems(state, { items }) {
        state.items = items
    },

    //删除购物车中的商品
    deleteCartItem(state, id) {
        let index = state.items.findIndex(item => item.id === id);
        if (index > -1)
            state.items.splice(index, 1);
    }
}

//getters
const getters = {
    //计算购物车中所有商品的总价
    cartTotalPrice: (state) => {
        return state.items.reduce((total, product) => {
            return total + product.price * product.quantity
        }, 0)
    },
    //计算购物车中单项商品的价格
    cartItemPrice: (state) => (id) => {
        if (state.items.length > 0) {
            const cartItem = state.items.find(item => item.id === id);
            if (cartItem) {
                return cartItem.price * cartItem.quantity;
            }
        }
    },
    //获取购物车中商品的数量
    itemsCount: (state) => {
        return state.items.length;
    }
}

//actions
const actions = {
    //增加任意数量的商品到购物车
    addProductToCart({ state, commit },
        { id, imgUrl, title, price, inventory, quantity }) {
        if (inventory > 0) {
            const cartItem = state.items.find(item => item.id == id);
            if (!cartItem) {
                commit('pushProductToCart', { id, imgUrl, title, price, quantity })
            } else {
                commit('incrementItemQuantity', { id, quantity })
            }
        }
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    getters,
    actions
}

items 数组用于保存购物车中所有商品信息的状态属性。
接下来,编辑 store 目录下的 index.js ,导入 cart 模块。如下:

store/index.js

import { createStore } from 'vuex'
import cart from './modules/cart'
import createPersistedState from "vuex-persistedstate"

export default new Vuex.Store({
  modules: {
    cart
  },
  plugins: [createPersistedState()]
})

在刷新浏览器窗口时,store 中存储的状态信息会被重置,这样就会导致加入购物车中的商品信息丢失。所以一般会选择一种浏览器端持久存储方案解决这个问题,比较常见且简单的方案就是 localStorage ,保存在 store 中的状态信息也要同步加入 localStorage ,在刷新浏览器窗口前,或者用用户重新访问网站时,从 localStorage 中读取状态信息保存到 store 中。
在整个应用期间,需要考虑各种情况下 store 与 localStorage 数据同步的问题,这比较麻烦。为此,可以使用一个第三方的插件解决 store 与 localStorage 数据同步的问题,即 vuex-persistedstate 插件。
首先安装 vuex-persistedstate 插件,在 Visual Studio Code 的终端窗口中执行以下命令进行安装。

npm install vuex-persistedstate -S

vuex-persistedstate 插件的使用非常简单,只需要两句代码就可以实现 store 的持久化存储,这会将整个 store 的状态以 vuex 为键名存储到 localStorage 中。
如果只想持久化存储 store 中的部分状态信息,那么可以在调用 createPersistedState() 方法时传递一个选项对象,在该选项对象的 reducer() 函数中返回要存储的数据。例如:

plugins:[createPersistedState({
  reducer (data){
    return {
      // 设置只存储 cart 模块中的状态
      cart:data.cart,
      // 或者设置只存储 cart 模块中的 items 数据
      // products:data.cart.items
    }
  }
})]

reducer() 函数的 data 参数是完整的 state 对象。
如果想改变底层使用的存储机制,如使用 sessioniStorage,那么可以在选项对象中通过 storage 指定。代码如下:

plugins:[createPersistedState({
  reducer (data){
  	storage:window.sessionStorage,
    ...
  }
})]

配置好 Vuex 的状态管理后,就可以开始编写购物车组件了。

17.8.2 购物车组件

在 views 目录下新建 ShoppingCart。如下:

views/ShoppingCart.vue

<template>
    <div class="shoppingCart">
        <table>
            <tr>
                <th></th>
                <th>商品名称</th>
                <th>单价</th>
                <th>数量</th>
                <th>金额</th>
                <th>操作</th>
            </tr>
            <tr v-for="book in books" :key="book.id">
                <td><img :src="book.imgUrl"></td>
                <td>
                    <router-link :to="{ name: 'book', params: { id: book.id } }" target="_blank">
                        {{ book.title }}
                    </router-link>
                </td>
                <td>{{ currency(book.price) }}</td>
                <td>
                    <button @click="handleSubtract(book)">-</button>
                    {{ book.quantity }}
                    <button @click="handleAdd(book.id)">+</button>
                </td>
                <td>{{ currency(cartItemPrice(book.id)) }}</td>
                <td>
                    <button @click="deleteCartItem(book.id)">删除</button>
                </td>
            </tr>
        </table>
        <p>
            <span><button class="checkout" @click="checkout">结算</button></span>
            <span>总价:{{ currency(cartTotalPrice) }}</span>
        </p>
    </div>
</template>

<script>
import { mapGetters, mapState, mapMutations } from 'vuex'
export default {
    name: "ShoppingCart",
    inject: ['currency'],
    computed: {
        ...mapState('cart', {
            books: 'items'
        }),
        ...mapGetters('cart', [
            'cartItemPrice',
            'cartTotalPrice'
        ])
    },

    methods: {
        itemPrice(price, count) {
            return price * count;
        },
        ...mapMutations('cart', [
            'deleteCartItem',
            'incrementItemQuantity',
            'setCartItems'
        ]),
        handleAdd(id) {
            this.incrementItemQuantity({ id: id, quantity: 1 });
        },
        handleSubtract(book) {
            let quantity = book.quantity - 1;

            if (quantity <= 0) {
                this.deleteCartItem(book.id);
            }
            else
                this.incrementItemQuantity({ id: book.id, quantity: -1 });
        },
        checkout() {
            this.$router.push("/check");
        }
    }
};
</script>
<style scoped>
.shoppingCart {
    text-align: center;
    margin-left: 45px;
    width: 96%;
    margin-top: 70px;
}

.shoppingCart table {
    border: solid 1px black;
    width: 100%;
    background-color: #eee;

}

.shoppingCart th {
    height: 50px;
}

.shoppingCart th,
.shoppingCart td {
    border-bottom: solid 1px #ddd;
    text-align: center;
}

.shoppingCart span {
    float: right;
    padding-right: 15px;
}

.shoppingCart img {
    width: 60px;
    height: 60px;
}

.shoppingCart .checkout {
    float: right;
    width: 60px;
    height: 30px;
    margin: 0;
    border: none;
    color: white;
    background-color: red;
    cursor: pointer;
}
</style>

ShoppingCart 组件提供了两种方式删除购物车中的某项商品:
(1)单击“删除”按钮,将直接删除购物车中的该商品
(2)用户单击数量下的减号按钮时,如果判断数量减一后为零,则删除该商品

17.9 结算页面

在购物车页面中单击“结算”按钮,则进入结算页面,结算页面再一次列出购物车中的所有商品,不同的是,在结算页面不能再对商品进行修改。
在 views 目录下新建 Checkout.vue。如下:

views/checkout.vue

<template>
    <div class="shoppingCart">
        <h1 v-if="success">{{ msg }}</h1>
        <table>
            <caption>商品结算</caption>
            <tr>
                <th></th>
                <th>商品名称</th>
                <th>单价</th>
                <th>数量</th>
                <th>金额</th>
            </tr>
            <tr v-for="book in books" :key="book.id">
                <td><img :src="book.imgUrl"></td>
                <td>
                    <router-link :to="{ name: 'book', params: { id: book.id } }" target="_blank">
                        {{ book.title }}
                    </router-link>
                </td>
                <td>{{ currency(book.price) }}</td>
                <td>
                    {{ book.quantity }}
                </td>
                <td>{{ currency(cartItemPrice(book.id)) }}</td>
            </tr>
        </table>
        <p>
            <span><button class="pay" @click="pay">付款</button></span>
            <span>总价:{{ currency(cartTotalPrice) }}</span>
        </p>
    </div>
</template>

<script>
import { mapGetters, mapState, mapMutations } from 'vuex'
export default {
    name: "Checkout",
    data() {
        return {
            success: false,
            msg: '付款成功!'
        };
    },
    inject: ['currency'],
    computed: {
        ...mapState('cart', {
            books: 'items'
        }),
        ...mapGetters('cart', [
            'cartItemPrice',
            'cartTotalPrice'
        ])
    },

    methods: {
        itemPrice(price, count) {
            return price * count;
        },
        ...mapMutations('cart', [
            'setCartItems'
        ]),

        pay() {
            this.setCartItems({ items: [] });
            this.success = true;
        }
    }
};
</script>
<style scoped>
.shoppingCart {
    text-align: center;
    margin-left: 45px;
    width: 96%;
    margin-top: 70px;
}

.shoppingCart h1 {
    color: red;
}

.shoppingCart table {
    border: solid 1px black;
    width: 100%;
    background-color: #eee;

}

.shoppingCart table>caption {
    font-size: 1.5em;
    font-weight: bold;
    margin: 5px 0 8px 0;
}

.shoppingCart th {
    height: 50px;
}

.shoppingCart th,
.shoppingCart td {
    border-bottom: solid 1px #ddd;
    text-align: center;
}

.shoppingCart span {
    float: right;
    padding-right: 15px;
}

.shoppingCart img {
    width: 60px;
    height: 60px;
}

.shoppingCart .pay {
    float: right;
    width: 60px;
    height: 30px;
    margin: 0;
    border: none;
    color: white;
    background-color: red;
    cursor: pointer;
}
</style>

在线支付涉及各个支付平台或银联的调用接口,所以本项目的购物车流程到这一步就结束了,当用户单击“付款”按钮时,只是简单地清空购物车,稍后提示用户“付款成功”。

17.10 用户管理

在实际场景中,当用户提交购物订单准备结算时,系统会判断用户是否已经登录,如果没有登录,会提示用户先进行登录,本节实现用户注册和用户登录组件。

17.10.1 用户状态管理配置

用户登录后的状态需要保存,不仅可以用于向用户显示欢迎信息,还可以用于对受保护的资源进行权限验证。同样,用户的状态存储也使用 Vuex 管理。
在 store/modules 目录下新建 user.js 。如下:

store/modules/user.js

const state = {
    user: null
}
// mutations
const mutations = {
    saveUser(state, { username, id }) {
        state.user = { username, id }
    },
    deleteUser(state) {
        state.user = null;
    }
}

export default {
    namespaced: true,
    state,
    mutations,
}

对于前端,存储用户名和用户 ID 已经足以,像用户中心等功能的实现,是需要重新向服务端去请求数据的。
编辑 store/index.js 文件,导入 user 模块,并在 modules 选项下进行注册。如下:

store/index.js

import { createStore } from 'vuex'

import cart from './modules/cart'
import user from './modules/user'
import createPersistedState from "vuex-persistedstate"

export default createStore({
  modules: {
    cart,
    user
  },
  plugins: [createPersistedState()]
})

17.10.2 用户注册组件

当用户单击 Header 组件中的 “注册”链接时,将跳转到用户注册页面。
在 components 目录下新建 UserRegister.vue 。如下:

components/UserRegister.vue

<template>
    <div class="register">
        <form>
            <div class="lable">
                <label class="error">{{ message }}</label>
                <input name="username" type="text" v-model="username" placeholder="请输入用户名" />
                <input type="password" v-model.trim="password" placeholder="请输入密码" />
                <input type="password" v-model.trim="password2" placeholder="请输入确认密码" />
                <input type="tel" v-model.trim="mobile" placeholder="请输入手机号" />
            </div>
            <div class="submit">
                <input type="submit" @click.prevent="register" value="注册" />
            </div>
        </form>
    </div>
</template>

<script>
import { mapMutations } from 'vuex';
export default {
    name: "UserRegister",
    props: [""],
    data() {
        return {
            username: "",
            password: "",
            password2: "",
            mobile: "",
            message: ''
        };
    },

    watch: {
        username(newVal) {
            // 取消上一次请求
            if (newVal) {
                this.cancelRequest();
                this.axios.get("/user/" + newVal, {
                        cancelToken: new this.axios.CancelToken(
                            cancel => this.cancel = cancel
                        )
                    })
                    .then(response => {
                        if (response.data.code == 200) {
                            let isExist = response.data.data;
                            if (isExist) {
                                this.message = "该用户名已经存在";
                            } else {
                                this.message = "";
                            }
                        }
                    })
                    .catch(error => {
                        if (this.axios.isCancel(error)) {
                            //如果是请求被取消产生的错误,输出取消请求的原因
                            console.log("请求取消:", error.message);
                            //alert(error.message);
                            //throw new Error("请求取消:" + error.message)
                        } else {
                            //处理错误
                            console.log(error);
                            //throw new Error(error.message)
                        }
                    });
            }
        }
    },
    methods: {
        register() {
            this.message = '';
            if (!this.checkForm())
                return;
            this.axios.post("/user/register",
                { username: this.username, password: this.password, mobile: this.mobile })
                .then(response => {
                    if (response.data.code === 200) {
                        this.saveUser(response.data.data);
                        this.username = '';
                        this.password = '';
                        this.password2 = '';
                        this.mobile = '';
                        this.$router.push("/");
                    } else if (response.data.code === 500) {
                        this.message = "用户注册失败";
                    }
                })
                .catch(error => {
                    alert(error.message)
                })
        },
        cancelRequest() {
            if (typeof this.cancel === "function") {
                this.cancel("终止请求");
            }
        },
        checkForm() {
            if (!this.username || !this.password || !this.password2 || !this.mobile) {
                this.$msgBox.show({ title: "所有字段不能为空" });
                return false;
            }
            if (this.password !== this.password2) {
                this.$msgBox.show({ title: "密码和确认密码必须相同" });
                return false;
            }
            return true;
        },
        ...mapMutations('user', [
            'saveUser'
        ])
    },
};
</script>
<style scoped>
.register {
    margin: 5em auto 0;
    width: 44%;
}

.register input {
    padding: 15px;
    width: 94%;
    font-size: 1.1em;
    margin: 18px 0px;
    color: gray;
    float: left;
    cursor: pointer;
    font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
    outline: none;
    font-weight: 600;
    margin-left: 3px;
    background: #eee;
    transition: all 0.3s ease-out;
    border: solid 1px #ccc;
}

.register input:hover {
    color: rgb(180, 86, 9);
    border-left: solid 6px #40A46F;
}

.register .submit {
    padding: 5px 4px;
    text-align: center;
}

.register input[type="submit"] {
    padding: 17px 17px;
    color: #fff;
    float: right;
    font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
    background: #40a46f;
    border: solid 1px #40a46f;
    cursor: pointer;
    font-size: 18px;
    transition: all 0.5s ease-out;
    outline: none;
    width: 100%;
}

.register .submit input[type="submit"]:hover {
    background: #07793d;
    border: solid 1px #07793d;
}

.register .error {
    color: red;
    font-weight: bold;
    font-size: 1.1em;
}
</style>

说明:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

红框处在这里实现了一个功能,当用户输入用户名时,实时去服务端检测该用户名是否已经存在,如果存在,则提示用户,这是通过 Vue 的监听器来实现的。
不过由于 v-model 指令内部实现机制的原因(对于文本输入框,默认绑定的是 input 事件),如果用户快速输入或快速用退格键删除用户名时,监听器将触发多次,由此导致频繁地向服务端发起请求。为了解决这个问题,可以利用 axios 的 cancel token 取消重复的请求。
使用 axios 发送请求时,可以传递一个配置对象,在配置对象中使用 cacelToken 选项,通过传递一个 executor() 函数到 CancelToken 的构造函数中创建 cancel token。
将 cancel() 函数保存为组件实例的方法,之后如果要取消请求,调用 this.cancel() 即可。cancel() 函数可以接收一个可选的消息字符串参数,用于给出取消请求的原因。同一个 cancel token 可以取消多个请求。
在发生错误时,可以在 catch() 方法中使用 this.axios.isCancel(error) 判断该错误是否是由取消请求而引发的。
当然,这里也可以通过修改 v-model 的监听事件为 change 解决快速输入和删除导致的重复请求问题,只需要给 v-model 指令添加 .lazy 修饰符即可。
用户名是否已注册的判断,请求的服务端数据接口如下:
http://111.229.37.167/api/user/{用户名}
返回的数据结构如下:

{
	"code": 200,
	"data": true  //如果要注册的用户名存在,则返回 false
}

用户注册请求的服务端数据接口如下:
http://111.229.37.167/api/user/register。
需要采用 Post() 方法向该接口发起请求,提交的数据是一个 JSON 格式的对象,该对象要包含 username、password 和 mobile 三个字段。
返回的数据结构如下:

{
  "code":200,
  "data":{
    "id":18,
    "username":"小鱼儿",
    "password":"1234",
    "mobile":"13222222222"
  }
}

实际开发时,服务端不把密码返回给前端,如果前端需要用到密码,则可以采用加密形式传输。
当用户注册成功后,将用户名和 ID 保存到 store 中,并跳转到根目录下,即网站的首页。然后 Header 组件会自动渲染出用户名,显示欢迎信息。

17.10.3 用户登录组件

当用户单击 Header 组件中的“登录”链接时,将跳转到用户登录页面。
在 components 目录下新建 UserLogin.vue 。如下:

components/UserLogin.vue

<template>
    <div class="login">
        <div class="error">{{ message }}</div>
        <form>
            <div class="lable">
                <input name="username" type="text" v-model.trim="username" placeholder="请输入用户名" />
                <input type="password" v-model.trim="password" placeholder="请输入密码" />
            </div>
            <div class="submit">
                <input type="submit" @click.prevent="login" value="登录" />
            </div>
        </form>
    </div>
</template>

<script>
import { mapMutations } from 'vuex';
export default {
    name: "UserLogin",
    data() {
        return {
            username: '',
            password: '',
            message: ''
        };
    },
    methods: {
        login() {
            this.message = '';
            if (!this.checkForm())
                return;
            this.axios.post("/user/login",
                { username: this.username, password: this.password })
                .then(response => {
                    if (response.data.code === 200) {
                        this.saveUser(response.data.data);
                        this.username = '';
                        this.password = '';
                        //如果存在查询参数
                        if (this.$route.query.redirect) {
                            const redirect = this.$route.query.redirect;
                            //跳转至进入登录页前的路由
                            this.$router.replace(redirect);
                        } else {
                            // 否则跳转至首页
                            this.$router.replace('/');
                        }
                    } else if (response.data.code === 500) {
                        this.message = "用户登录失败";
                    } else if (response.data.code === 400) {
                        this.message = "用户名或密码错误";
                    }
                })
                .catch(error => {
                    console.log(error.message);
                })
        },
        ...mapMutations('user', [
            'saveUser'
        ]),
        checkForm() {
            if (!this.username || !this.password) {
                this.$msgBox.show({ title: "用户名和密码不能为空" });
                return false;
            }
            return true;
        }
    }
};
</script>
<style scoped>
.login {
    margin: 5em auto 0;
    width: 44%;
}

.login input {
    padding: 15px;
    width: 94%;
    font-size: 1.1em;
    margin: 18px 0px;
    color: gray;
    float: left;
    cursor: pointer;
    font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
    outline: none;
    font-weight: 600;
    margin-left: 3px;
    background: #eee;
    transition: all 0.3s ease-out;
    border: solid 1px #ccc;
}

.login input:hover {
    color: rgb(180, 86, 9);
    border-left: solid 6px #40A46F;
}

.login {
    padding: 5px 4px;
    text-align: center;
}

input[type="submit"] {
    padding: 17px 17px;
    color: #fff;
    float: right;
    font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
    background: #40a46f;
    border: solid 1px #40a46f;
    cursor: pointer;
    font-size: 18px;
    transition: all 0.5s ease-out;
    outline: none;
    width: 100%;
}

.submit input[type="submit"]:hover {
    background: #07793d;
    border: solid 1px #07793d;
}

.login .error {
    color: red;
    font-weight: bold;
    font-size: 1.1em;
}
</style>

用户登录组件并不复杂,值得一提的就是在用户登录后需要跳转到进入登录页面前的路由,这会让用户体验更好,实现方式已经在 14.10.1 小节介绍过了,本项目也是利用 beforeEach() 注册的全局前置守卫保存用户登录前的路由路径,可以参看 17.11 节。
用户登录请求的数据接口如下:
http://111.229.37.167/api/user/login
同样是以 Post() 方法发起请求,提交的数据是一个 JSON 格式的对象,该对象要包含 username 和 password 两个字段。
返回的数据格式与用户注册返回的数据格式相同。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【北大青鸟天府校区的Java专业怎么样?】

北大青鸟天府校区是北大青鸟总部在西南投资兴办的重点校区&#xff0c;拥有极为强大的外部教育资源和就业资源&#xff0c;使咱们的学员能够享受更好、更完善的教学资源。超过23年的办学经验&#xff0c;培养了超过10000位学员&#xff0c;保障就业&#xff0c;学员就业率达到9…

[入门必看]数据结构2.3:线性表的链式表示

[入门必看]数据结构2.3&#xff1a;线性表的链式表示第二章 线性表2.3 线性表的链式表示知识总览2.3.1 单链表的定义2.3.2_1 单链表的插入删除2.3.2_2 单链表的查找2.3.2_3 单链表的建立2.3.3 双链表2.3.4 循环链表2.3.5 静态链表2.3.6 顺序表和链表的比较2.3.1 单链表的定义单…

JUC高级四:Java内存模型之JMM

JUC高级四:Java内存模型之JMM 1. 计算机硬件存储体系(JMM为什么诞生) 因为有这么多级的缓存(cpu和物理主内存的速度不一致的)&#xff0c;CPU的运行并不是直接操作内存而是先把内存里边的数据读到缓存&#xff0c;而内存的读和写操作的时候就会造成不一致的问题 在我们cpu寄存…

WebRTC开源库内部调用abort函数引发程序发生闪退问题的排查

目录 1、初始问题描述 2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来 3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题 4、更新库之后Debug程序启动时就发生异常&#xff0c;程序闪退 5、VS调试时看不到有效的函数调用堆…

十大Python可视化工具,太强了

今天介绍Python当中十大可视化工具&#xff0c;每一个都独具特色&#xff0c;惊艳一方。 Matplotlib Matplotlib 是 Python 的一个绘图库&#xff0c;可以绘制出高质量的折线图、散点图、柱状图、条形图等等。它也是许多其他可视化库的基础。 import matplotlib.pyplot as p…

OpenCV入门(二十)快速学会OpenCV 19 对象测量

OpenCV入门&#xff08;二十&#xff09;快速学会OpenCV 19 对象测量1.对象测量2.多边形拟合3.计算对象中心作者&#xff1a;Xiou 1.对象测量 opencv 中对象测量包括&#xff1a; 如面积&#xff0c;周长&#xff0c;质心&#xff0c;边界框等。 弧长与面积测量&#xff1b; …

《LKD3粗读笔记》(4)进程调度

1、多任务 什么是多任务操作系统&#xff1f; 能同时并发地交互执行多个进程。注意是并发而不是并行。特别地&#xff0c;在多处理机机器上可以实现真正意义上的并行&#xff0c;因为它长了多个脑子多任务操作系统有哪些分类&#xff1f; 非抢占式多任务&#xff08;cooperati…

【云原生】Kubernetes(k8s)部署 MySQL+Dubbo+Nacos服务

一、说明二、部署 MySQL三、部署 Nacos四、部署 Dubbo 服务4.1. 创建镜像仓库的密钥4.2. 部署 provider 服务4.3. 部署 consumer 服务五、测试一、说明 本文介绍基于 Kubernetes(k8s) 环境集成阿里云 私有镜像仓库 来部署一套 Dubbo Nacos 的微服务系统&#xff0c;并使用 Ku…

7个最好的PDF编辑器,帮你像编辑Word一样编辑PDF

PDF 是具有数字思维的组织的重要交流工具。提供高效的工作流程和更好的安全性&#xff0c;可以创建重要文档并与客户、同事和员工共享。文档的布局已锁定&#xff0c;因此无论在什么设备上查看&#xff0c;格式都保持不变。这是让每个人保持一致的好方法——尤其是那些使用Micr…

C++中的引用

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C】 接下来就要开始进行C的学习路线了&#xff0c;听说这块的内容稍微难一些&#xff0c;不过我相信只要自己好好学习&#xff0c;态度…

java与Spring的循环依赖

java与Spring的循环依赖一、循环依赖是什么有什么危害二、循环依赖在Spring中的体现和类型三、Spirng如何解决循环依赖四、总结一、循环依赖是什么有什么危害 什么是循环依赖 java中循环依赖用一张图来说就是下图&#xff1a;在对象的创建过程中多个对象形成了依赖闭环&#xf…

初识linux之管道

一、进程间通信的概念大家都知道&#xff0c;进程是具有独立性的&#xff0c;因为一个程序运行起来生成进程时&#xff0c;也会生成它的进程结构体&#xff0c;即PCB&#xff0c;然后然后通过进程结构体中的结构体指针找到它的虚拟地址空间&#xff0c;然后再通过它的页表映射到…

C语言——字符函数和字符串函数【详解】(一)

文章目录函数介绍1.strlen2.strcpy3. strcat4. strcmp5. strncpy6. strncat7. strncmp8. strstr函数介绍 求字符串长度 strlen 长度不受限制的字符串函数&#xff08;使用时不安全&#xff09; strcpy strcat strcmp 长度受限制的字符串函数介绍&#xff08;与长度不受限制函数…

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(9)

目录 写在前面&#xff1a; 题目&#xff1a;P1025 [NOIP2001 提高组] 数的划分 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; 代…

【数据结构】哈希表

目录 1、哈希表 1.1 哈希表的简介 1.2 降低哈希冲突率 1.3 解决哈希冲突 1.3.1 闭散列 1.3.2 开散列&#xff08;哈希桶&#xff09; 1、哈希表 1.1 哈希表的简介 假设我们目前有一组数据&#xff0c;我们要从这组数据中找到指定的 key 值&#xff0c;那么咱们目…

【Java集合面试宝典】HashMap的put流程和特性?HashMap的扩容机制?原理— day08

目录 数组和链表分别适用于什么场景&#xff0c;为什么&#xff1f; 数组 链表 List和Set的区别 List和Map、Set的区别 HashMap 、HashTable 和TreeMap有什么区别&#xff1f; hashmap的特性 HashMap和HashTable有什么区别&#xff1f;&#xff08;必会&#xff09; J…

【数据结构】树的介绍

文章目录前言树的概念及结构树的概念树的表示树在实际中的运用二叉树的概念及结构二叉树的概念现实中的二叉树特殊的二叉树二叉树的性质二叉树的储存结构顺序存储链式存储写在最后前言 &#x1f6a9;本章给大家介绍一下树。树的难度相对于前面的数据结构来说&#xff0c;又高了…

ESP32设备驱动-HDC1080温度湿度传感器驱动

HDC1080温度湿度传感器驱动 文章目录 HDC1080温度湿度传感器驱动1、HDC1080介绍2、硬件准备3、软件准备4、驱动实现1、HDC1080介绍 HDC1080 是一款集成温度传感器的数字湿度传感器,可在极低功耗下提供出色的测量精度。 HDC1080 在很宽的电源范围内工作,是一种低成本、低功耗…

“提效”|教你用ChatGPT玩数据

ChatGPT与数据分析&#xff08;二&#xff09; 上文给简单聊了一下为什么ChatGPT不能取代数据分析师&#xff0c;本文我们来深入感受一下如何让GPT帮助数据分析师“提效”。 场景一&#xff1a;SQL取数 背景&#xff1a;多数数据分析师都要用SQL语言从数据库中提取数据&#x…

ctfshow web入门 命令执行29-33

1.web29eval()函数是把所有字符串当作php代码去执行&#xff0c;这题过滤了flag,使用通配符绕过过滤应该要注意文件中没有重名的文件&#xff0c;或一部分是一样的文件payload:cecho%20nl flag.php; #官方解法&#xff0c;反引号表示执行系统命令&#xff0c;nl为linux系统命令…